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 /chrome/common/accelerated_surface_mac.cc | |
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
Diffstat (limited to 'chrome/common/accelerated_surface_mac.cc')
-rw-r--r-- | chrome/common/accelerated_surface_mac.cc | 334 |
1 files changed, 334 insertions, 0 deletions
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); +} |