diff options
author | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-10 14:26:16 +0000 |
---|---|---|
committer | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-10 14:26:16 +0000 |
commit | f8ee26e45e8a04bb75a3e7b0449b8acf3efb04e2 (patch) | |
tree | faf7390df7bc035edbda8458897432329040de04 | |
parent | b5e18b1d24c065bdeb874abca3e9c41b42b5192f (diff) | |
download | chromium_src-f8ee26e45e8a04bb75a3e7b0449b8acf3efb04e2.zip chromium_src-f8ee26e45e8a04bb75a3e7b0449b8acf3efb04e2.tar.gz chromium_src-f8ee26e45e8a04bb75a3e7b0449b8acf3efb04e2.tar.bz2 |
Implement the CoreAnimation drawing model for plug-ins, sharing IPC and some rendering code with the GPU Plug-in. The drawing model negotiation is currently disabled so this should have no visible impact to plug-ins.
BUG=32012
TEST=make sure the pepper GPU plug-ins still work.
Review URL: http://codereview.chromium.org/673001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41158 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_mac.mm | 7 | ||||
-rw-r--r-- | chrome/chrome_common.gypi | 2 | ||||
-rw-r--r-- | chrome/common/accelerated_surface_mac.cc | 334 | ||||
-rw-r--r-- | chrome/common/accelerated_surface_mac.h | 108 | ||||
-rw-r--r-- | chrome/common/plugin_messages_internal.h | 6 | ||||
-rw-r--r-- | chrome/plugin/webplugin_delegate_stub.h | 2 | ||||
-rw-r--r-- | chrome/plugin/webplugin_proxy.cc | 22 | ||||
-rw-r--r-- | chrome/plugin/webplugin_proxy.h | 9 | ||||
-rw-r--r-- | chrome/renderer/webplugin_delegate_proxy.cc | 29 | ||||
-rw-r--r-- | chrome/renderer/webplugin_delegate_proxy.h | 8 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 346 | ||||
-rw-r--r-- | webkit/glue/plugins/plugin_host.cc | 42 | ||||
-rw-r--r-- | webkit/glue/plugins/webplugin_delegate_impl.h | 39 | ||||
-rw-r--r-- | webkit/glue/plugins/webplugin_delegate_impl_mac.mm | 93 | ||||
-rw-r--r-- | webkit/glue/webplugin.h | 20 | ||||
-rw-r--r-- | webkit/tools/test_shell/mac/accelerated_surface_stub.cc | 52 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gypi | 1 | ||||
-rw-r--r-- | webkit/webkit.gyp | 5 |
18 files changed, 766 insertions, 359 deletions
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 079cecd..0d20f6b 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm @@ -83,6 +83,10 @@ const size_t kMaxTooltipLength = 1024; forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp { renderWidgetHostView_->DrawGPUPluginInstances(glContext); + [super drawInCGLContext:glContext + pixelFormat:pixelFormat + forLayerTime:timeInterval + displayTime:timeStamp]; } @end @@ -579,6 +583,7 @@ void RenderWidgetHostViewMac::DrawGPUPluginInstances(CGLContextObj context) { glOrtho(0, rect.width(), rect.height(), 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + plugin_container_manager_.Draw(context); } @@ -1469,7 +1474,7 @@ extern NSString *NSTextInputReplacementRangeAttributeName; // TODO(hbono): Even though many input method works without implementing // this method, we need to save a copy of the string in the setMarkedText // method and create a NSAttributedString with the given range. - NOTIMPLEMENTED(); + // http://crbug.com/37715 return nil; } diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index 934e097..4fb4c85 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -23,6 +23,8 @@ # .cc, .h, and .mm files under chrome/common that are used on all # platforms, including both 32-bit and 64-bit Windows. # Test files are not included. + 'common/accelerated_surface_mac.cc', + 'common/accelerated_surface_mac.h', 'common/app_mode_constants_mac.h', 'common/app_mode_constants_mac.mm', 'common/bindings_policy.h', diff --git a/chrome/common/accelerated_surface_mac.cc b/chrome/common/accelerated_surface_mac.cc new file mode 100644 index 0000000..d12ecb6 --- /dev/null +++ b/chrome/common/accelerated_surface_mac.cc @@ -0,0 +1,334 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/accelerated_surface_mac.h" + +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "chrome/common/io_surface_support_mac.h" + +AcceleratedSurface::AcceleratedSurface() + : gl_context_(NULL), + pbuffer_(NULL), + surface_width_(0), + surface_height_(0), + texture_(0), + fbo_(0), + depth_stencil_renderbuffer_(0), + bound_fbo_(0), + bound_renderbuffer_(0) { +} + +bool AcceleratedSurface::Initialize() { + // Create a 1x1 pbuffer and associated context to bootstrap things + static const CGLPixelFormatAttribute attribs[] = { + (CGLPixelFormatAttribute) kCGLPFAPBuffer, + (CGLPixelFormatAttribute) 0 + }; + CGLPixelFormatObj pixel_format; + GLint num_pixel_formats; + if (CGLChoosePixelFormat(attribs, + &pixel_format, + &num_pixel_formats) != kCGLNoError) { + DLOG(ERROR) << "Error choosing pixel format."; + return false; + } + if (!pixel_format) { + return false; + } + CGLContextObj context; + CGLError res = CGLCreateContext(pixel_format, 0, &context); + CGLDestroyPixelFormat(pixel_format); + if (res != kCGLNoError) { + DLOG(ERROR) << "Error creating context."; + return false; + } + CGLPBufferObj pbuffer; + if (CGLCreatePBuffer(1, 1, + GL_TEXTURE_2D, GL_RGBA, + 0, &pbuffer) != kCGLNoError) { + CGLDestroyContext(context); + DLOG(ERROR) << "Error creating pbuffer."; + return false; + } + if (CGLSetPBuffer(context, pbuffer, 0, 0, 0) != kCGLNoError) { + CGLDestroyContext(context); + CGLDestroyPBuffer(pbuffer); + DLOG(ERROR) << "Error attaching pbuffer to context."; + return false; + } + gl_context_ = context; + pbuffer_ = pbuffer; + // Now we're ready to handle SetWindowSize calls, which will + // allocate and/or reallocate the IOSurface and associated offscreen + // OpenGL structures for rendering. + return true; +} + +void AcceleratedSurface::Destroy() { + // Release the old TransportDIB in the browser. + if (dib_free_callback_.get() && transport_dib_.get()) { + dib_free_callback_->Run(transport_dib_->id()); + } + transport_dib_.reset(); + if (gl_context_) + CGLDestroyContext(gl_context_); + if (pbuffer_) + CGLDestroyPBuffer(pbuffer_); +} + +// Call after making changes to the surface which require a visual update. +// Makes the rendering show up in other processes. +void AcceleratedSurface::SwapBuffers() { + if (bound_fbo_ != fbo_) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); + } + if (io_surface_.get() != NULL) { + // Bind and unbind the framebuffer to make changes to the + // IOSurface show up in the other process. + glFlush(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); + } else if (transport_dib_.get() != NULL) { + // Pre-Mac OS X 10.6, fetch the rendered image from the FBO and copy it + // into the TransportDIB. + // TODO(dspringer): There are a couple of options that can speed this up. + // First is to use async reads into a PBO, second is to use SPI that + // allows many tasks to access the same CGSSurface. + void* pixel_memory = transport_dib_->memory(); + if (pixel_memory) { + // Note that glReadPixels does an implicit glFlush(). + glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + glReadPixels(0, + 0, + surface_width_, + surface_height_, + GL_BGRA, // This pixel format should have no conversion. + GL_UNSIGNED_INT_8_8_8_8_REV, + pixel_memory); + } + } + if (bound_fbo_ != fbo_) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_); + } +} + +static void AddBooleanValue(CFMutableDictionaryRef dictionary, + const CFStringRef key, + bool value) { + CFDictionaryAddValue(dictionary, key, + (value ? kCFBooleanTrue : kCFBooleanFalse)); +} + +static void AddIntegerValue(CFMutableDictionaryRef dictionary, + const CFStringRef key, + int32 value) { + CFNumberRef number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value); + CFDictionaryAddValue(dictionary, key, number); +} + +void AcceleratedSurface::AllocateRenderBuffers(GLenum target, + int32 width, int32 height) { + if (!texture_) { + // Generate the texture object. + glGenTextures(1, &texture_); + glBindTexture(target, texture_); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + // Generate and bind the framebuffer object. + glGenFramebuffersEXT(1, &fbo_); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); + bound_fbo_ = fbo_; + // Generate (but don't bind) the depth buffer -- we don't need + // this bound in order to do offscreen rendering. + glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_); + } + + // Reallocate the depth buffer. + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_renderbuffer_); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, + GL_DEPTH24_STENCIL8_EXT, + width, + height); + + // Unbind the renderbuffers. + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, bound_renderbuffer_); + + // Make sure that subsequent set-up code affects the render texture. + glBindTexture(target, texture_); +} + +bool AcceleratedSurface::SetupFrameBufferObject(GLenum target) { + if (bound_fbo_ != fbo_) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); + } + GLenum fbo_status; + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + target, + texture_, + 0); + fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT) { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + depth_stencil_renderbuffer_); + fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + } + // Attach the depth and stencil buffer. + if (fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT) { + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + 0x8D20, // GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER_EXT, + depth_stencil_renderbuffer_); + fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + } + if (bound_fbo_ != fbo_) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_); + } + return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT; +} + +bool AcceleratedSurface::MakeCurrent() { + if (CGLGetCurrentContext() != gl_context_) { + if (CGLSetCurrentContext(gl_context_) != kCGLNoError) { + DLOG(ERROR) << "Unable to make gl context current."; + return false; + } + } + return true; +} + +void AcceleratedSurface::Clear(const gfx::Rect& rect) { + glClearColor(1.0, 1.0, 1.0, 1.0); + glViewport(0, 0, rect.width(), rect.height()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, rect.width(), 0, rect.height(), -1, 1); + glClear(GL_COLOR_BUFFER_BIT); +} + +uint64 AcceleratedSurface::SetSurfaceSize(int32 width, int32 height) { + if (surface_width_ == width && surface_height_ == height) { + // Return 0 to indicate to the caller that no new backing store + // allocation occurred. + return 0; + } + + IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); + if (!io_surface_support) + return 0; // Caller can try using SetWindowSizeForTransportDIB(). + + if (!MakeCurrent()) + return 0; + + // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on + // Mac OS X and is required for IOSurface interoperability. + GLenum target = GL_TEXTURE_RECTANGLE_ARB; + AllocateRenderBuffers(target, width, height); + + // Allocate a new IOSurface, which is the GPU resource that can be + // shared across processes. + scoped_cftyperef<CFMutableDictionaryRef> properties; + properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + AddIntegerValue(properties, + io_surface_support->GetKIOSurfaceWidth(), width); + AddIntegerValue(properties, + io_surface_support->GetKIOSurfaceHeight(), height); + AddIntegerValue(properties, + io_surface_support->GetKIOSurfaceBytesPerElement(), 4); + AddBooleanValue(properties, + io_surface_support->GetKIOSurfaceIsGlobal(), true); + // I believe we should be able to unreference the IOSurfaces without + // synchronizing with the browser process because they are + // ultimately reference counted by the operating system. + io_surface_.reset(io_surface_support->IOSurfaceCreate(properties)); + + // Don't think we need to identify a plane. + GLuint plane = 0; + io_surface_support->CGLTexImageIOSurface2D(gl_context_, + target, + GL_RGBA, + width, + height, + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, + io_surface_.get(), + plane); + // Set up the frame buffer object. + SetupFrameBufferObject(target); + surface_width_ = width; + surface_height_ = height; + + // Now send back an identifier for the IOSurface. We originally + // intended to send back a mach port from IOSurfaceCreateMachPort + // but it looks like Chrome IPC would need to be modified to + // properly send mach ports between processes. For the time being we + // make our IOSurfaces global and send back their identifiers. On + // the browser process side the identifier is reconstituted into an + // IOSurface for on-screen rendering. + return io_surface_support->IOSurfaceGetID(io_surface_); +} + +TransportDIB::Handle AcceleratedSurface::SetTransportDIBSize( + int32 width, int32 height) { + if (surface_width_ == width && surface_height_ == height) { + // Return an invalid handle to indicate to the caller that no new backing + // store allocation occurred. + return TransportDIB::DefaultHandleValue(); + } + surface_width_ = width; + surface_height_ = height; + + // Release the old TransportDIB in the browser. + if (dib_free_callback_.get() && transport_dib_.get()) { + dib_free_callback_->Run(transport_dib_->id()); + } + transport_dib_.reset(); + + // Ask the renderer to create a TransportDIB. + size_t dib_size = width * 4 * height; // 4 bytes per pixel. + TransportDIB::Handle dib_handle; + if (dib_alloc_callback_.get()) { + dib_alloc_callback_->Run(dib_size, &dib_handle); + } + if (!TransportDIB::is_valid(dib_handle)) { + // If the allocator fails, it means the DIB was not created in the browser, + // so there is no need to run the deallocator here. + return TransportDIB::DefaultHandleValue(); + } + transport_dib_.reset(TransportDIB::Map(dib_handle)); + if (transport_dib_.get() == NULL) { + // TODO(dspringer): if the Map() fails, should the deallocator be run so + // that the DIB is deallocated in the browser? + return TransportDIB::DefaultHandleValue(); + } + + // Set up the render buffers and reserve enough space on the card for the + // framebuffer texture. + GLenum target = GL_TEXTURE_RECTANGLE_ARB; + AllocateRenderBuffers(target, width, height); + glTexImage2D(target, + 0, // mipmap level 0 + GL_RGBA8, // internal pixel format + width, + height, + 0, // 0 border + GL_BGRA, // Used for consistency + GL_UNSIGNED_INT_8_8_8_8_REV, + NULL); // No data, just reserve room on the card. + SetupFrameBufferObject(target); + return transport_dib_->handle(); +} + +void AcceleratedSurface::SetTransportDIBAllocAndFree( + Callback2<size_t, TransportDIB::Handle*>::Type* allocator, + Callback1<TransportDIB::Id>::Type* deallocator) { + dib_alloc_callback_.reset(allocator); + dib_free_callback_.reset(deallocator); +} diff --git a/chrome/common/accelerated_surface_mac.h b/chrome/common/accelerated_surface_mac.h new file mode 100644 index 0000000..e8c27af --- /dev/null +++ b/chrome/common/accelerated_surface_mac.h @@ -0,0 +1,108 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_ACCELERATED_SURFACE_MAC_H_ +#define CHROME_COMMON_ACCELERATED_SURFACE_MAC_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include <OpenGL/OpenGL.h> + +#include "base/callback.h" +#include "base/scoped_cftyperef.h" +#include "base/scoped_ptr.h" +#include "chrome/common/transport_dib.h" + +namespace gfx { +class Rect; +} + +// Encapsulates an accelerated GL surface that can be shared across processes +// on systems that support it (10.6 and above). For systems that do not, it +// uses a regular dib. There will either be a GL Context or a TransportDIB, +// never both. + +class AcceleratedSurface { + public: + AcceleratedSurface(); + virtual ~AcceleratedSurface() { } + + // Set up internal buffers. Returns false upon failure. + bool Initialize(); + // Tear down. Must be called before destructor to prevent leaks. + void Destroy(); + + // These methods are used only when there is a GL surface. + + // Sets the accelerated surface to the given size, creating a new one if + // the height or width changes. Returns a unique id of the IOSurface to + // which the surface is bound, or 0 if no changes were made or an error + // occurred. MakeCurrent() will have been called on the new surface. + uint64 SetSurfaceSize(int32 width, int32 height); + // Sets the GL context to be the current one for drawing. Returns true if + // it succeeded. + bool MakeCurrent(); + // Clear the surface to all white. Assumes the caller has already called + // MakeCurrent(). + void Clear(const gfx::Rect& rect); + // Call after making changes to the surface which require a visual update. + // Makes the rendering show up in other processes. + void SwapBuffers(); + CGLContextObj context() { return gl_context_; } + + // These methods are only used when there is a transport DIB. + + // Sets the transport DIB to the given size, creating a new one if the + // height or width changes. Returns a handle to the new DIB, or a default + // handle if no changes were made. + TransportDIB::Handle SetTransportDIBSize(int32 width, int32 height); + // Sets the methods to use for allocating and freeing memory for the + // transport DIB. + void SetTransportDIBAllocAndFree( + Callback2<size_t, TransportDIB::Handle*>::Type* allocator, + Callback1<TransportDIB::Id>::Type* deallocator); + + private: + // Helper function to generate names for the backing texture, render buffers + // and FBO. On return, the resulting buffer names can be attached to |fbo_|. + // |target| is the target type for the color buffer. + void AllocateRenderBuffers(GLenum target, int32 width, int32 height); + + // Helper function to attach the buffers previously allocated by a call to + // AllocateRenderBuffers(). On return, |fbo_| can be used for + // rendering. |target| must be the same value as used in the call to + // AllocateRenderBuffers(). Returns |true| if the resulting framebuffer + // object is valid. + bool SetupFrameBufferObject(GLenum target); + + CGLContextObj gl_context_; + CGLPBufferObj pbuffer_; + // Either |io_surface_| or |transport_dib_| is a valid pointer, but not both. + // |io_surface_| is non-NULL if the IOSurface APIs are supported (Mac OS X + // 10.6 and later). + // TODO(dspringer,kbr): Should the GPU backing store be encapsulated in its + // own class so all this implementation detail is hidden? + scoped_cftyperef<CFTypeRef> io_surface_; + // TODO(dspringer): If we end up keeping this TransportDIB mechanism, this + // should really be a scoped_ptr_malloc<>, with a deallocate functor that + // runs |dib_free_callback_|. I was not able to figure out how to + // make this work (or even compile). + scoped_ptr<TransportDIB> transport_dib_; + int32 surface_width_; + int32 surface_height_; + GLuint texture_; + GLuint fbo_; + GLuint depth_stencil_renderbuffer_; + // For tracking whether the default framebuffer / renderbuffer or + // ones created by the end user are currently bound + // TODO(kbr): Need to property hook up and track the OpenGL state and hook + // up the notion of the currently bound FBO. + GLuint bound_fbo_; + GLuint bound_renderbuffer_; + // Allocate a TransportDIB in the renderer. + scoped_ptr<Callback2<size_t, TransportDIB::Handle*>::Type> + dib_alloc_callback_; + scoped_ptr<Callback1<TransportDIB::Id>::Type> dib_free_callback_; +}; + +#endif // CHROME_COMMON_ACCELERATED_SURFACE_MAC_H_ diff --git a/chrome/common/plugin_messages_internal.h b/chrome/common/plugin_messages_internal.h index 3a5594c..08e1aba 100644 --- a/chrome/common/plugin_messages_internal.h +++ b/chrome/common/plugin_messages_internal.h @@ -416,6 +416,12 @@ IPC_BEGIN_MESSAGES(PluginHost) int32 /* height */, TransportDIB::Handle /* handle to the TransportDIB */) + // Synthesize a fake window handle for the plug-in to identify the instance + // to the browser, allowing mapping to a surface for hardware accelleration + // of plug-in content. The browser generates the handle which is then set on + // the plug-in. + IPC_MESSAGE_ROUTED0(PluginHostMsg_BindFakePluginWindowHandle) + // This message, used only on 10.6 and later, is sent from the // plug-in process to the renderer process to indicate that the GPU // plugin allocated a new IOSurface object of the given width and diff --git a/chrome/plugin/webplugin_delegate_stub.h b/chrome/plugin/webplugin_delegate_stub.h index 1bbd04d..64dea79d 100644 --- a/chrome/plugin/webplugin_delegate_stub.h +++ b/chrome/plugin/webplugin_delegate_stub.h @@ -33,7 +33,7 @@ class WebInputEvent; class WebPluginDelegateImpl; // Converts the IPC messages from WebPluginDelegateProxy into calls to the -// actual WebPluginDelegate object. +// actual WebPluginDelegateImpl object. class WebPluginDelegateStub : public IPC::Channel::Listener, public IPC::Message::Sender, public base::RefCounted<WebPluginDelegateStub> { diff --git a/chrome/plugin/webplugin_proxy.cc b/chrome/plugin/webplugin_proxy.cc index ffda5c1..e7826fd 100644 --- a/chrome/plugin/webplugin_proxy.cc +++ b/chrome/plugin/webplugin_proxy.cc @@ -576,6 +576,28 @@ void WebPluginProxy::SetDeferResourceLoading(unsigned long resource_id, Send(new PluginHostMsg_DeferResourceLoading(route_id_, resource_id, defer)); } +#if defined(OS_MACOSX) +void WebPluginProxy::BindFakePluginWindowHandle() { + Send(new PluginHostMsg_BindFakePluginWindowHandle(route_id_)); +} + +void WebPluginProxy::AcceleratedFrameBuffersDidSwap( + gfx::PluginWindowHandle window) { + // TODO(pinkerton): Rename this message. + Send(new PluginHostMsg_GPUPluginBuffersSwapped(route_id_, window)); +} + +void WebPluginProxy::SetAcceleratedSurface(gfx::PluginWindowHandle window, + int32 width, + int32 height, + uint64 accelerated_surface_identifier) { + // TODO(pinkerton): Rename this message. + Send(new PluginHostMsg_GPUPluginSetIOSurface(route_id_, window, width, + height, + accelerated_surface_identifier)); +} +#endif + void WebPluginProxy::OnPaint(const gfx::Rect& damaged_rect) { child_process_logging::SetActiveURL(page_url_); diff --git a/chrome/plugin/webplugin_proxy.h b/chrome/plugin/webplugin_proxy.h index 0beb9cb..9329d6c 100644 --- a/chrome/plugin/webplugin_proxy.h +++ b/chrome/plugin/webplugin_proxy.h @@ -132,6 +132,15 @@ class WebPluginProxy : public webkit_glue::WebPlugin { webkit_glue::WebPluginResourceClient* resource_client); gfx::NativeViewId containing_window() { return containing_window_; } +#if defined(OS_MACOSX) + virtual void BindFakePluginWindowHandle(); + virtual void AcceleratedFrameBuffersDidSwap(gfx::PluginWindowHandle window); + virtual void SetAcceleratedSurface(gfx::PluginWindowHandle window, + int32 width, + int32 height, + uint64 accelerated_surface_identifier); +#endif + private: bool Send(IPC::Message* msg); diff --git a/chrome/renderer/webplugin_delegate_proxy.cc b/chrome/renderer/webplugin_delegate_proxy.cc index b00f1b5..10bf1f8 100644 --- a/chrome/renderer/webplugin_delegate_proxy.cc +++ b/chrome/renderer/webplugin_delegate_proxy.cc @@ -402,6 +402,8 @@ void WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) { OnDeferResourceLoading) #if defined(OS_MACOSX) + IPC_MESSAGE_HANDLER(PluginHostMsg_BindFakePluginWindowHandle, + OnBindFakePluginWindowHandle); IPC_MESSAGE_HANDLER(PluginHostMsg_UpdateGeometry_ACK, OnUpdateGeometry_ACK) // Used only on 10.6 and later. @@ -1255,25 +1257,38 @@ WebPluginDelegateProxy::CreateSeekableResourceClient( return proxy; } -CommandBufferProxy* WebPluginDelegateProxy::CreateCommandBuffer() { -#if defined(ENABLE_GPU) #if defined(OS_MACOSX) - // We need to synthesize a fake window handle for this nested - // delegate to identify the instance of the GPU plugin back to the - // browser. +void WebPluginDelegateProxy::OnBindFakePluginWindowHandle() { + BindFakePluginWindowHandle(); +} + +// Synthesize a fake window handle for the plug-in to identify the instance +// to the browser, allowing mapping to a surface for hardware acceleration +// of plug-in content. The browser generates the handle which is then set on +// the plug-in. Returns true if it successfully sets the window handle on the +// plug-in. +bool WebPluginDelegateProxy::BindFakePluginWindowHandle() { gfx::PluginWindowHandle fake_window = NULL; if (render_view_) fake_window = render_view_->AllocateFakePluginWindowHandle(); // If we aren't running on 10.6, this allocation will fail. if (!fake_window) - return NULL; + return false; OnSetWindow(fake_window); if (!Send(new PluginMsg_SetFakeGPUPluginWindowHandle(instance_id_, fake_window))) { - return NULL; + return false; } + return true; +} #endif +CommandBufferProxy* WebPluginDelegateProxy::CreateCommandBuffer() { +#if defined(ENABLE_GPU) +#if defined(OS_MACOSX) + if (!BindFakePluginWindowHandle()) + return NULL; +#endif int command_buffer_id; if (!Send(new PluginMsg_CreateCommandBuffer(instance_id_, &command_buffer_id))) { diff --git a/chrome/renderer/webplugin_delegate_proxy.h b/chrome/renderer/webplugin_delegate_proxy.h index 212758e..cdeb62a 100644 --- a/chrome/renderer/webplugin_delegate_proxy.h +++ b/chrome/renderer/webplugin_delegate_proxy.h @@ -149,6 +149,7 @@ class WebPluginDelegateProxy void OnDeferResourceLoading(unsigned long resource_id, bool defer); #if defined(OS_MACOSX) + void OnBindFakePluginWindowHandle(); void OnUpdateGeometry_ACK(int ack_key); void OnGPUPluginSetIOSurface(gfx::PluginWindowHandle window, int32 width, @@ -188,6 +189,13 @@ class WebPluginDelegateProxy void WillDestroyWindow(); #if defined(OS_MACOSX) + // Synthesize a fake window handle for the plug-in to identify the instance + // to the browser, allowing mapping to a surface for hardware acceleration + // of plug-in content. The browser generates the handle which is then set on + // the plug-in. Returns true if it successfully sets the window handle on the + // plug-in. + bool BindFakePluginWindowHandle(); + // The Mac TransportDIB implementation uses base::SharedMemory, which // cannot be disposed of if an in-flight UpdateGeometry message refers to // the shared memory file descriptor. The old_transport_dibs_ map holds diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index e07dc99..b9637e9 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -31,13 +31,7 @@ // XWindowWrapper is stubbed out for unit-tests. #include "gpu/command_buffer/service/x_utils.h" #elif defined(OS_MACOSX) -// The following two #includes CAN NOT go above the inclusion of -// gl_utils.h and therefore glew.h regardless of what the Google C++ -// style guide says. -#include <CoreFoundation/CoreFoundation.h> // NOLINT -#include <OpenGL/OpenGL.h> // NOLINT -#include "base/scoped_cftyperef.h" -#include "chrome/common/io_surface_support_mac.h" +#include "chrome/common/accelerated_surface_mac.h" #endif namespace gpu { @@ -564,20 +558,6 @@ class GLES2DecoderImpl : public GLES2Decoder { #undef GLES2_CMD_OP -#if defined(OS_MACOSX) - // Helper function to generate names for the backing texture, render buffers - // and FBO. On return, the resulting buffer names can be attached to |fbo_|. - // |target| is the target type for the color buffer. - void AllocateRenderBuffers(GLenum target, int32 width, int32 height); - - // Helper function to attach the buffers previously allocated by a call to - // AllocateRenderBuffers(). On return, |fbo_| can be used for - // rendering. |target| must be the same value as used in the call to - // AllocateRenderBuffers(). Returns |true| if the resulting framebuffer - // object is valid. - bool SetupFrameBufferObject(GLenum target); -#endif - // Current GL error bits. uint32 error_bits_; @@ -626,32 +606,7 @@ class GLES2DecoderImpl : public GLES2Decoder { HGLRC gl_context_; HPBUFFERARB pbuffer_; #elif defined(OS_MACOSX) - CGLContextObj gl_context_; - CGLPBufferObj pbuffer_; - // Either |io_surface_| or |transport_dib_| is a valid pointer, but not both. - // |io_surface_| is non-NULL if the IOSurface APIs are supported (Mac OS X - // 10.6 and later). - // TODO(dspringer,kbr): Should the GPU backing store be encapsulated in its - // own class so all this implementation detail is hidden? - scoped_cftyperef<CFTypeRef> io_surface_; - // TODO(dspringer): If we end up keeping this TransportDIB mechanism, this - // should really be a scoped_ptr_malloc<>, with a deallocate functor that - // runs |dib_free_callback_|. I was not able to figure out how to - // make this work (or even compile). - scoped_ptr<TransportDIB> transport_dib_; - int32 surface_width_; - int32 surface_height_; - GLuint texture_; - GLuint fbo_; - GLuint depth_stencil_renderbuffer_; - // For tracking whether the default framebuffer / renderbuffer or - // ones created by the end user are currently bound - GLuint bound_fbo_; - GLuint bound_renderbuffer_; - // Allocate a TransportDIB in the renderer. - scoped_ptr<Callback2<size_t, TransportDIB::Handle*>::Type> - dib_alloc_callback_; - scoped_ptr<Callback1<TransportDIB::Id>::Type> dib_free_callback_; + AcceleratedSurface surface_; #endif bool anti_aliased_; @@ -684,16 +639,6 @@ GLES2DecoderImpl::GLES2DecoderImpl(ContextGroup* group) gl_device_context_(NULL), gl_context_(NULL), pbuffer_(NULL), -#elif defined(OS_MACOSX) - gl_context_(NULL), - pbuffer_(NULL), - surface_width_(0), - surface_height_(0), - texture_(0), - fbo_(0), - depth_stencil_renderbuffer_(0), - bound_fbo_(0), - bound_renderbuffer_(0), #endif anti_aliased_(false) { } @@ -1001,13 +946,7 @@ bool GLES2DecoderImpl::MakeCurrent() { #elif defined(OS_LINUX) return window()->MakeCurrent(); #elif defined(OS_MACOSX) - if (CGLGetCurrentContext() != gl_context_) { - if (CGLSetCurrentContext(gl_context_) != kCGLNoError) { - DLOG(ERROR) << "Unable to make gl context current."; - return false; - } - } - return true; + return surface_.MakeCurrent(); #else NOTREACHED(); return false; @@ -1113,48 +1052,7 @@ bool GLES2DecoderImpl::InitPlatformSpecific() { if (!window()->Initialize()) return false; #elif defined(OS_MACOSX) - // Create a 1x1 pbuffer and associated context to bootstrap things - static const CGLPixelFormatAttribute attribs[] = { - (CGLPixelFormatAttribute) kCGLPFAPBuffer, - (CGLPixelFormatAttribute) 0 - }; - CGLPixelFormatObj pixel_format; - GLint num_pixel_formats; - if (CGLChoosePixelFormat(attribs, - &pixel_format, - &num_pixel_formats) != kCGLNoError) { - DLOG(ERROR) << "Error choosing pixel format."; - return false; - } - if (!pixel_format) { - return false; - } - CGLContextObj context; - CGLError res = CGLCreateContext(pixel_format, 0, &context); - CGLDestroyPixelFormat(pixel_format); - if (res != kCGLNoError) { - DLOG(ERROR) << "Error creating context."; - return false; - } - CGLPBufferObj pbuffer; - if (CGLCreatePBuffer(1, 1, - GL_TEXTURE_2D, GL_RGBA, - 0, &pbuffer) != kCGLNoError) { - CGLDestroyContext(context); - DLOG(ERROR) << "Error creating pbuffer."; - return false; - } - if (CGLSetPBuffer(context, pbuffer, 0, 0, 0) != kCGLNoError) { - CGLDestroyContext(context); - CGLDestroyPBuffer(pbuffer); - DLOG(ERROR) << "Error attaching pbuffer to context."; - return false; - } - gl_context_ = context; - pbuffer_ = pbuffer; - // Now we're ready to handle SetWindowSize calls, which will - // allocate and/or reallocate the IOSurface and associated offscreen - // OpenGL structures for rendering. + return surface_.Initialize(); #endif return true; @@ -1240,151 +1138,12 @@ void GLES2DecoderImpl::DestroyPlatformSpecific() { } #if defined(OS_MACOSX) -#if !defined(UNIT_TEST) -static void AddBooleanValue(CFMutableDictionaryRef dictionary, - const CFStringRef key, - bool value) { - CFDictionaryAddValue(dictionary, key, - (value ? kCFBooleanTrue : kCFBooleanFalse)); -} - -static void AddIntegerValue(CFMutableDictionaryRef dictionary, - const CFStringRef key, - int32 value) { - CFNumberRef number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value); - CFDictionaryAddValue(dictionary, key, number); -} -#endif // !defined(UNIT_TEST) - -void GLES2DecoderImpl::AllocateRenderBuffers(GLenum target, - int32 width, int32 height) { - if (!texture_) { - // Generate the texture object. - glGenTextures(1, &texture_); - glBindTexture(target, texture_); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - // Generate and bind the framebuffer object. - glGenFramebuffersEXT(1, &fbo_); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); - bound_fbo_ = fbo_; - // Generate (but don't bind) the depth buffer -- we don't need - // this bound in order to do offscreen rendering. - glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_); - } - - // Reallocate the depth buffer. - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_renderbuffer_); - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, - GL_DEPTH24_STENCIL8_EXT, - width, - height); - - // Unbind the renderbuffers. - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, bound_renderbuffer_); - - // Make sure that subsequent set-up code affects the render texture. - glBindTexture(target, texture_); -} - -bool GLES2DecoderImpl::SetupFrameBufferObject(GLenum target) { - if (bound_fbo_ != fbo_) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); - } - GLenum fbo_status; - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, - GL_COLOR_ATTACHMENT0_EXT, - target, - texture_, - 0); - fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if (fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT) { - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, - GL_DEPTH_ATTACHMENT_EXT, - GL_RENDERBUFFER_EXT, - depth_stencil_renderbuffer_); - fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - } - // Attach the depth and stencil buffer. - if (fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT) { - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, - GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER_EXT, - depth_stencil_renderbuffer_); - fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - } - if (bound_fbo_ != fbo_) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_); - } - return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT; -} uint64 GLES2DecoderImpl::SetWindowSizeForIOSurface(int32 width, int32 height) { #if defined(UNIT_TEST) return 0; #else - if (surface_width_ == width && surface_height_ == height) { - // Return 0 to indicate to the caller that no new backing store - // allocation occurred. - return 0; - } - - IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); - if (!io_surface_support) - return 0; // Caller can try using SetWindowSizeForTransportDIB(). - - if (!MakeCurrent()) - return 0; - - // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on - // Mac OS X and is required for IOSurface interoperability. - GLenum target = GL_TEXTURE_RECTANGLE_ARB; - AllocateRenderBuffers(target, width, height); - - // Allocate a new IOSurface, which is the GPU resource that can be - // shared across processes. - scoped_cftyperef<CFMutableDictionaryRef> properties; - properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - AddIntegerValue(properties, - io_surface_support->GetKIOSurfaceWidth(), width); - AddIntegerValue(properties, - io_surface_support->GetKIOSurfaceHeight(), height); - AddIntegerValue(properties, - io_surface_support->GetKIOSurfaceBytesPerElement(), 4); - AddBooleanValue(properties, - io_surface_support->GetKIOSurfaceIsGlobal(), true); - // I believe we should be able to unreference the IOSurfaces without - // synchronizing with the browser process because they are - // ultimately reference counted by the operating system. - io_surface_.reset(io_surface_support->IOSurfaceCreate(properties)); - - // Don't think we need to identify a plane. - GLuint plane = 0; - io_surface_support->CGLTexImageIOSurface2D(gl_context_, - target, - GL_RGBA, - width, - height, - GL_BGRA, - GL_UNSIGNED_INT_8_8_8_8_REV, - io_surface_.get(), - plane); - // Set up the frame buffer object. - SetupFrameBufferObject(target); - surface_width_ = width; - surface_height_ = height; - - // Now send back an identifier for the IOSurface. We originally - // intended to send back a mach port from IOSurfaceCreateMachPort - // but it looks like Chrome IPC would need to be modified to - // properly send mach ports between processes. For the time being we - // make our IOSurfaces global and send back their identifiers. On - // the browser process side the identifier is reconstituted into an - // IOSurface for on-screen rendering. - return io_surface_support->IOSurfaceGetID(io_surface_); + return surface_.SetSurfaceSize(width, height); #endif // !defined(UNIT_TEST) } @@ -1393,61 +1152,14 @@ TransportDIB::Handle GLES2DecoderImpl::SetWindowSizeForTransportDIB( #if defined(UNIT_TEST) return TransportDIB::DefaultHandleValue(); #else - if (surface_width_ == width && surface_height_ == height) { - // Return an invalid handle to indicate to the caller that no new backing - // store allocation occurred. - return TransportDIB::DefaultHandleValue(); - } - surface_width_ = width; - surface_height_ = height; - - // Release the old TransportDIB in the browser. - if (dib_free_callback_.get() && transport_dib_.get()) { - dib_free_callback_->Run(transport_dib_->id()); - } - transport_dib_.reset(); - - // Ask the renderer to create a TransportDIB. - size_t dib_size = width * 4 * height; // 4 bytes per pixel. - TransportDIB::Handle dib_handle; - if (dib_alloc_callback_.get()) { - dib_alloc_callback_->Run(dib_size, &dib_handle); - } - if (!TransportDIB::is_valid(dib_handle)) { - // If the allocator fails, it means the DIB was not created in the browser, - // so there is no need to run the deallocator here. - return TransportDIB::DefaultHandleValue(); - } - transport_dib_.reset(TransportDIB::Map(dib_handle)); - if (transport_dib_.get() == NULL) { - // TODO(dspringer): if the Map() fails, should the deallocator be run so - // that the DIB is deallocated in the browser? - return TransportDIB::DefaultHandleValue(); - } - - // Set up the render buffers and reserve enough space on the card for the - // framebuffer texture. - GLenum target = GL_TEXTURE_RECTANGLE_ARB; - AllocateRenderBuffers(target, width, height); - glTexImage2D(target, - 0, // mipmap level 0 - GL_RGBA8, // internal pixel format - width, - height, - 0, // 0 border - GL_BGRA, // Used for consistency - GL_UNSIGNED_INT_8_8_8_8_REV, - NULL); // No data, just reserve room on the card. - SetupFrameBufferObject(target); - return transport_dib_->handle(); + return surface_.SetTransportDIBSize(width, height); #endif // !defined(UNIT_TEST) } void GLES2DecoderImpl::SetTransportDIBAllocAndFree( Callback2<size_t, TransportDIB::Handle*>::Type* allocator, Callback1<TransportDIB::Id>::Type* deallocator) { - dib_alloc_callback_.reset(allocator); - dib_free_callback_.reset(deallocator); + surface_.SetTransportDIBAllocAndFree(allocator, deallocator); } #endif // defined(OS_MACOSX) @@ -1461,15 +1173,7 @@ void GLES2DecoderImpl::Destroy() { DCHECK(window()); window()->Destroy(); #elif defined(OS_MACOSX) - // Release the old TransportDIB in the browser. - if (dib_free_callback_.get() && transport_dib_.get()) { - dib_free_callback_->Run(transport_dib_->id()); - } - transport_dib_.reset(); - if (gl_context_) - CGLDestroyContext(gl_context_); - if (pbuffer_) - CGLDestroyPBuffer(pbuffer_); + surface_.Destroy(); #endif DestroyPlatformSpecific(); @@ -1728,37 +1432,9 @@ void GLES2DecoderImpl::DoSwapBuffers() { DCHECK(window()); window()->SwapBuffers(); #elif defined(OS_MACOSX) - if (bound_fbo_ != fbo_) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); - } - if (io_surface_.get() != NULL) { - // Bind and unbind the framebuffer to make changes to the - // IOSurface show up in the other process. - glFlush(); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_); - } else if (transport_dib_.get() != NULL) { - // Pre-Mac OS X 10.6, fetch the rendered image from the FBO and copy it - // into the TransportDIB. - // TODO(dspringer): There are a couple of options that can speed this up. - // First is to use async reads into a PBO, second is to use SPI that - // allows many tasks to access the same CGSSurface. - void* pixel_memory = transport_dib_->memory(); - if (pixel_memory) { - // Note that glReadPixels does an implicit glFlush(). - glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); - glReadPixels(0, - 0, - surface_width_, - surface_height_, - GL_BGRA, // This pixel format should have no conversion. - GL_UNSIGNED_INT_8_8_8_8_REV, - pixel_memory); - } - } - if (bound_fbo_ != fbo_) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_); - } + // TODO(kbr): Need to property hook up and track the OpenGL state and hook + // up the notion of the currently bound FBO. + surface_.SwapBuffers(); #endif if (swap_buffers_callback_.get()) { swap_buffers_callback_->Run(); diff --git a/webkit/glue/plugins/plugin_host.cc b/webkit/glue/plugins/plugin_host.cc index 0db965c..4cd5b3b 100644 --- a/webkit/glue/plugins/plugin_host.cc +++ b/webkit/glue/plugins/plugin_host.cc @@ -9,6 +9,9 @@ #include "base/scoped_ptr.h" #include "base/string_piece.h" #include "base/string_util.h" +#if defined(OS_MACOSX) +#include "base/sys_info.h" +#endif #include "base/sys_string_conversions.h" #include "net/base/net_util.h" #include "third_party/WebKit/WebKit/chromium/public/WebBindings.h" @@ -36,6 +39,21 @@ static NPAPI::PluginInstance* FindInstance(NPP id) { return reinterpret_cast<NPAPI::PluginInstance*>(id->ndata); } +#if defined(OS_MACOSX) +// Returns true if the OS supports shared accelerated surfaces via IOSurface. +// This is true on Snow Leopard and higher. +static bool SupportsSharingAcceleratedSurfaces() { + int32 major, minor, bugfix; + base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); +#if PINK_USE_COREANIMATION + // TODO(pinkerton): enable this, http://crbug.com/32012 + return major > 10 || (major == 10 && minor > 5); +#else + return false; +#endif +} +#endif + namespace NPAPI { scoped_refptr<PluginHost> PluginHost::singleton_; @@ -757,7 +775,10 @@ NPError NPN_GetValue(NPP id, NPNVariable variable, void* value) { } #ifndef NP_NO_QUICKDRAW case NPNVsupportsQuickDrawBool: { - // we do not admit to supporting the QuickDraw drawing model. + // We do not admit to supporting the QuickDraw drawing model. The logic + // here is that our QuickDraw plugin support is so rudimentary that we + // only want to use it as a fallback to keep plugins from crashing: if a + // plugin knows enough to ask, we want them to use CoreGraphics. NPBool* supports_qd = reinterpret_cast<NPBool*>(value); *supports_qd = FALSE; rv = NPERR_NO_ERROR; @@ -775,9 +796,16 @@ NPError NPN_GetValue(NPP id, NPNVariable variable, void* value) { rv = NPERR_NO_ERROR; break; } - case NPNVsupportsOpenGLBool: case NPNVsupportsCoreAnimationBool: { - // we do not support these drawing and event models. + // We only support the Core Animation model on 10.6 and higher + NPBool* supports_model = reinterpret_cast<NPBool*>(value); + *supports_model = SupportsSharingAcceleratedSurfaces() ? TRUE : FALSE; + rv = NPERR_NO_ERROR; + break; + } + case NPNVsupportsOpenGLBool: { + // This drawing model was never widely supported, and we don't plan to + // support it. NPBool* supports_model = reinterpret_cast<NPBool*>(value); *supports_model = FALSE; rv = NPERR_NO_ERROR; @@ -838,14 +866,14 @@ NPError NPN_SetValue(NPP id, NPPVariable variable, void* value) { return NPERR_GENERIC_ERROR; #if defined(OS_MACOSX) case NPPVpluginDrawingModel: { - // We only admit to supporting the CoreGraphics drawing model. The logic - // here is that our QuickDraw plugin support is so rudimentary that we - // only want to use it as a fallback to keep plugins from crashing: if - // a plugin knows enough to ask, we want them to use CoreGraphics. int model = reinterpret_cast<int>(value); if (model == NPDrawingModelCoreGraphics) { plugin->set_drawing_model(model); return NPERR_NO_ERROR; + } else if (model == NPDrawingModelCoreAnimation && + SupportsSharingAcceleratedSurfaces()) { + plugin->set_drawing_model(model); + return NPERR_NO_ERROR; } return NPERR_GENERIC_ERROR; } diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h index 1327464..8cf0d03 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl.h +++ b/webkit/glue/plugins/webplugin_delegate_impl.h @@ -16,10 +16,15 @@ #include "base/gfx/rect.h" #include "base/ref_counted.h" #include "base/task.h" +#include "base/timer.h" #include "third_party/npapi/bindings/npapi.h" #include "webkit/glue/webcursor.h" #include "webkit/glue/webplugin_delegate.h" +#if defined(OS_MACOSX) +#include "chrome/common/accelerated_surface_mac.h" +#endif + #if defined(USE_X11) typedef struct _GdkDrawable GdkPixmap; #endif @@ -32,8 +37,19 @@ namespace WebKit { class WebMouseEvent; } -// An implementation of WebPluginDelegate that proxies all calls to -// the plugin process. +#if defined(OS_MACOSX) +class CoreAnimationRedrawTimerSource; +#ifdef __OBJC__ +@class CALayer; +@class CARenderer; +#else +class CALayer; +class CARenderer; +#endif +#endif + +// An implementation of WebPluginDelegate that runs in the plugin process, +// proxied from the renderer by WebPluginDelegateProxy. class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { public: enum PluginQuirks { @@ -146,6 +162,12 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { // Informs the delegate that the plugin set a Cocoa NSCursor. void SetNSCursor(NSCursor* cursor); + // Informs the browser about the updated accelerated drawing surface. + void UpdateAcceleratedSurface(); + + // Uses a CARenderer to draw the plug-in's layer in our OpenGL surface. + void DrawLayerInSurface(); + #ifndef NP_NO_CARBON // Indicates that it's time to send the plugin a null event. void FireIdleEvent(); @@ -157,10 +179,11 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { } #if defined(OS_MACOSX) - // On Mac OS X and for the GPU plugin only, this handle is a fake - // one and comes in from the outside world. + // Allow setting a "fake" window handle to associate this plug-in with + // an IOSurface in the browser. Used for accelerated drawing surfaces. void set_windowed_handle(gfx::PluginWindowHandle handle) { windowed_handle_ = handle; + UpdateAcceleratedSurface(); } #endif @@ -244,7 +267,7 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { // used for windowed plugins // Note: on Mac OS X, the only time the windowed handle is non-zero - // is the case of the GPU plugin, which uses a fake window handle to + // is the case of accelerated rendering, which uses a fake window handle to // identify itself back to the browser. It still performs all of its // work offscreen. gfx::PluginWindowHandle windowed_handle_; @@ -301,6 +324,10 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { #ifndef NP_NO_QUICKDRAW NP_Port qd_port_; #endif + CALayer* layer_; // Used for CA drawing mode. Weak, retained by plug-in. + AcceleratedSurface surface_; + CARenderer* renderer_; // Renders layer_ to surface_. + scoped_ptr<base::RepeatingTimer<WebPluginDelegateImpl> > redraw_timer_; #endif gfx::Rect window_rect_; gfx::Rect clip_rect_; @@ -356,7 +383,7 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { #ifndef NP_NO_CARBON // Moves our dummy window to match the current screen location of the plugin. void UpdateDummyWindowBounds(const gfx::Point& plugin_origin); - + #ifndef NP_NO_QUICKDRAW // Scrapes the contents of our dummy window into the given context. void ScrapeDummyWindowIntoContext(CGContextRef context); diff --git a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm index 0a1e84e..c944c64 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm +++ b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #import <Cocoa/Cocoa.h> +#import <QuartzCore/QuartzCore.h> #include "webkit/glue/plugins/webplugin_delegate_impl.h" @@ -16,7 +17,6 @@ #include "base/scoped_ptr.h" #include "base/stats_counters.h" #include "base/string_util.h" -#include "base/timer.h" #include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h" #include "webkit/default_plugin/plugin_impl.h" #include "webkit/glue/webplugin.h" @@ -49,6 +49,8 @@ using WebKit::WebInputEvent; using WebKit::WebMouseEvent; using WebKit::WebMouseWheelEvent; +const int kCoreAnimationRedrawPeriodMs = 20; // 50fps + // Important implementation notes: The Mac definition of NPAPI, particularly // the distinction between windowed and windowless modes, differs from the // Windows and Linux definitions. Most of those differences are @@ -169,16 +171,21 @@ class CarbonIdleEventSource { } // namespace +#pragma mark - + WebPluginDelegateImpl::WebPluginDelegateImpl( gfx::PluginWindowHandle containing_view, NPAPI::PluginInstance *instance) - : windowless_needs_set_window_(true), + : windowed_handle_(NULL), + windowless_needs_set_window_(true), // all Mac plugins are "windowless" in the Windows/X11 sense windowless_(true), plugin_(NULL), instance_(instance), parent_(containing_view), buffer_context_(NULL), + layer_(nil), + renderer_(nil), quirks_(0), have_focus_(false), focus_notifier_(NULL), @@ -256,6 +263,31 @@ void WebPluginDelegateImpl::PlatformInitialize() { #endif window_.type = NPWindowTypeDrawable; break; + case NPDrawingModelCoreAnimation: { // Assumes Cocoa event model. + window_.type = NPWindowTypeDrawable; + // Ask the plug-in for the CALayer it created for rendering content. Have + // the renderer tell the browser to create a "windowed plugin" to host + // the IOSurface. The surface itself will be created when the plug-in + // is sized. + CALayer* layer = nil; + NPError err = instance()->NPP_GetValue(NPPVpluginCoreAnimationLayer, + reinterpret_cast<void*>(&layer)); + if (!err) { + layer_ = layer; + plugin_->BindFakePluginWindowHandle(); + surface_.Initialize(); + UpdateAcceleratedSurface(); + renderer_ = [[CARenderer rendererWithCGLContext:surface_.context() + options:NULL] retain]; + [renderer_ setLayer:layer_]; + redraw_timer_.reset(new base::RepeatingTimer<WebPluginDelegateImpl>); + redraw_timer_->Start( + base::TimeDelta::FromMilliseconds(kCoreAnimationRedrawPeriodMs), + this, + &WebPluginDelegateImpl::DrawLayerInSurface); + } + break; + } default: NOTREACHED(); break; @@ -286,6 +318,12 @@ void WebPluginDelegateImpl::PlatformDestroyInstance() { if (instance()->event_model() == NPEventModelCarbon) CarbonIdleEventSource::SharedInstance()->UnregisterDelegate(this); #endif + if (redraw_timer_.get()) + redraw_timer_->Stop(); + [renderer_ release]; + renderer_ = nil; + layer_ = nil; + surface_.Destroy(); } void WebPluginDelegateImpl::UpdateGeometryAndContext( @@ -379,8 +417,32 @@ void WebPluginDelegateImpl::WindowlessUpdateGeometry( WindowlessSetWindow(true); } +void WebPluginDelegateImpl::DrawLayerInSurface() { + surface_.MakeCurrent(); + + surface_.Clear(window_rect_); + + // Ensure all changes are made before rendering. Not sure where the |-begin| + // comes from, but not doing this causes nothing to render. + [CATransaction commit]; + [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL]; + CGRect layerRect = [layer_ bounds]; + [renderer_ addUpdateRect:layerRect]; + [renderer_ render]; + [renderer_ endFrame]; + + surface_.SwapBuffers(); + plugin_->AcceleratedFrameBuffersDidSwap(windowed_handle()); +} + void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context, const gfx::Rect& damage_rect) { + // There is currently nothing to do for the Core Animation drawing model, + // but there have been discussions on the plugin-futures mailing list that + // might require future work. + if (instance()->drawing_model() == NPDrawingModelCoreAnimation) + return; + // If we somehow get a paint before we've set up the plugin buffer, bail. if (!buffer_context_) return; @@ -458,6 +520,8 @@ void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { SetWindowHasFocus(initial_window_focus_); } + UpdateAcceleratedSurface(); + DCHECK(err == NPERR_NO_ERROR); } @@ -580,6 +644,30 @@ void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) { } } +// Generate an IOSurface for accelerated drawing (but only in the case where a +// window handle has been set). Once the surface has been updated for the +// current size of the plug-in, tell the browser host view so it can adjust its +// bookkeeping and CALayer appropriately. +void WebPluginDelegateImpl::UpdateAcceleratedSurface() { + // Will only have a window handle when using the CoreAnimation drawing model. + if (!windowed_handle() || + instance()->drawing_model() != NPDrawingModelCoreAnimation) + return; + + [layer_ setFrame:CGRectMake(0, 0, + window_rect_.width(), window_rect_.height())]; + [renderer_ setBounds:[layer_ bounds]]; + + uint64 io_surface_id = surface_.SetSurfaceSize(window_rect_.width(), + window_rect_.height()); + if (io_surface_id) { + plugin_->SetAcceleratedSurface(windowed_handle(), + window_rect_.width(), + window_rect_.height(), + io_surface_id); + } +} + void WebPluginDelegateImpl::WindowFrameChanged(gfx::Rect window_frame, gfx::Rect view_frame) { instance()->set_window_frame(window_frame); @@ -600,6 +688,7 @@ void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) { void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) { window_rect_ = rect; + UpdateAcceleratedSurface(); PluginScreenLocationChanged(); } diff --git a/webkit/glue/webplugin.h b/webkit/glue/webplugin.h index d83601a..3f9c7e5 100644 --- a/webkit/glue/webplugin.h +++ b/webkit/glue/webplugin.h @@ -144,6 +144,26 @@ class WebPlugin { virtual void SetDeferResourceLoading(unsigned long resource_id, bool defer) = 0; +#if defined(OS_MACOSX) + // Synthesize a fake window handle for the plug-in to identify the instance + // to the browser, allowing mapping to a surface for hardware accelleration + // of plug-in content. The browser generates the handle which is then set on + // the plug-in. + virtual void BindFakePluginWindowHandle() {} + + // Tell the browser (via the renderer) to invalidate because the + // accelerated buffers have changed. + virtual void AcceleratedFrameBuffersDidSwap(gfx::PluginWindowHandle window) {} + + // Tell the renderer and browser to associate the given plugin handle with + // |accelerated_surface_identifier|. The geometry is used to resize any + // native "window" (which on the Mac is a CALayer). + virtual void SetAcceleratedSurface(gfx::PluginWindowHandle window, + int32 width, + int32 height, + uint64 accelerated_surface_identifier) {} +#endif + // Gets the WebPluginDelegate that implements the interface. // This API is only for use with Pepper, and is only overridden // by in-renderer implementations. diff --git a/webkit/tools/test_shell/mac/accelerated_surface_stub.cc b/webkit/tools/test_shell/mac/accelerated_surface_stub.cc new file mode 100644 index 0000000..77ca622 --- /dev/null +++ b/webkit/tools/test_shell/mac/accelerated_surface_stub.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is a complete and total hack intended to stub out some classes +// used by WebPluginDelegateImpl on Mac. Unfortunately, they live in +// chrome/common, so we can't compile them into TestShell. Instead, provide +// some stubs. It will need to be updated if new methods are added to +// AcceleratedSurface that get called from WebPluginDelegateImpl. It's not +// like plug-ins work in TestShell anyway. + +#include <CoreFoundation/CoreFoundation.h> +#include <OpenGL/OpenGL.h> + +#include "base/basictypes.h" +#include "base/gfx/rect.h" +#include "base/scoped_ptr.h" + +class TransportDIB { + public: + TransportDIB() { } + ~TransportDIB() { } +}; + +class AcceleratedSurface { + public: + AcceleratedSurface(); + virtual ~AcceleratedSurface(); + + bool Initialize(); + void Destroy(); + uint64 SetSurfaceSize(int32 width, int32 height); + bool MakeCurrent(); + void Clear(const gfx::Rect& rect); + void SwapBuffers(); + CGLContextObj context() { return NULL; } + private: + scoped_ptr<TransportDIB> ignore_; +}; + +AcceleratedSurface::AcceleratedSurface() { } +AcceleratedSurface::~AcceleratedSurface() { } +bool AcceleratedSurface::Initialize() { return false; } +void AcceleratedSurface::Destroy() { } +uint64 AcceleratedSurface::SetSurfaceSize(int32 width, int32 height) + { return 0; } +bool AcceleratedSurface::MakeCurrent() { return false; } +void AcceleratedSurface::Clear(const gfx::Rect& rect) { } +void AcceleratedSurface::SwapBuffers() { } + +// Needs to be instantiated in order to generate the dtor for the scoped_ptr. +AcceleratedSurface s; diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index a28daf2..a1bd3cb 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -48,6 +48,7 @@ ], 'msvs_guid': '77C32787-1B96-CB84-B905-7F170629F0AC', 'sources': [ + 'mac/accelerated_surface_stub.cc', 'mac/DumpRenderTreePasteboard.h', 'mac/DumpRenderTreePasteboard.m', 'mac/test_shell_webview.h', diff --git a/webkit/webkit.gyp b/webkit/webkit.gyp index 282bb3b..0754c9f 100644 --- a/webkit/webkit.gyp +++ b/webkit/webkit.gyp @@ -408,6 +408,11 @@ ['exclude', r'/mac_']], }, { # else: OS=="mac" 'sources/': [['exclude', 'plugin_(lib|list)_posix\\.cc$']], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/QuartzCore.framework', + ], + }, }], ['enable_gpu==1', { 'dependencies': [ |