diff options
Diffstat (limited to 'content/common/gpu/image_transport_surface_mac.cc')
| -rw-r--r-- | content/common/gpu/image_transport_surface_mac.cc | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/content/common/gpu/image_transport_surface_mac.cc b/content/common/gpu/image_transport_surface_mac.cc new file mode 100644 index 0000000..44e72e0 --- /dev/null +++ b/content/common/gpu/image_transport_surface_mac.cc @@ -0,0 +1,476 @@ +// Copyright (c) 2011 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. + +#if defined(ENABLE_GPU) + +#include "content/common/gpu/image_transport_surface.h" + +#include "base/mac/scoped_cftyperef.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/gpu/gpu_messages.h" +#include "ui/gfx/gl/gl_context.h" +#include "ui/gfx/gl/gl_bindings.h" +#include "ui/gfx/gl/gl_implementation.h" +#include "ui/gfx/gl/gl_surface_cgl.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/surface/io_surface_support_mac.h" + +namespace { + +// We are backed by an Pbuffer offscreen surface for the purposes of creating a +// context, but use FBOs to render to texture backed IOSurface +class IOSurfaceImageTransportSurface : public gfx::PbufferGLSurfaceCGL, + public ImageTransportSurface { + public: + IOSurfaceImageTransportSurface(GpuChannelManager* manager, + int32 render_view_id, + int32 renderer_id, + int32 command_buffer_id, + gfx::PluginWindowHandle handle); + + // GLSurface implementation + virtual bool Initialize() OVERRIDE; + virtual void Destroy() OVERRIDE; + virtual bool IsOffscreen() OVERRIDE; + virtual bool SwapBuffers() OVERRIDE; + virtual gfx::Size GetSize() OVERRIDE; + virtual void OnMakeCurrent(gfx::GLContext* context) OVERRIDE; + virtual unsigned int GetBackingFrameBufferObject() OVERRIDE; + + protected: + // ImageTransportSurface implementation + virtual void OnNewSurfaceACK(uint64 surface_id, + TransportDIB::Handle shm_handle) OVERRIDE; + virtual void OnBuffersSwappedACK() OVERRIDE; + virtual void OnResize(gfx::Size size) OVERRIDE; + + private: + virtual ~IOSurfaceImageTransportSurface() OVERRIDE; + + uint32 fbo_id_; + GLuint texture_id_; + + base::mac::ScopedCFTypeRef<CFTypeRef> io_surface_; + + // The id of |io_surface_| or 0 if that's NULL. + uint64 io_surface_id_; + + // Weak pointer to the context that this was last made current to. + gfx::GLContext* context_; + + gfx::Size size_; + + scoped_ptr<ImageTransportHelper> helper_; + + DISALLOW_COPY_AND_ASSIGN(IOSurfaceImageTransportSurface); +}; + +// We are backed by an Pbuffer offscreen surface for the purposes of creating a +// context, but use FBOs to render offscreen. +class TransportDIBImageTransportSurface : public gfx::PbufferGLSurfaceCGL, + public ImageTransportSurface { + public: + TransportDIBImageTransportSurface(GpuChannelManager* manager, + int32 render_view_id, + int32 renderer_id, + int32 command_buffer_id, + gfx::PluginWindowHandle handle); + + // GLSurface implementation + virtual bool Initialize() OVERRIDE; + virtual void Destroy() OVERRIDE; + virtual bool IsOffscreen() OVERRIDE; + virtual bool SwapBuffers() OVERRIDE; + virtual gfx::Size GetSize() OVERRIDE; + virtual void OnMakeCurrent(gfx::GLContext* context) OVERRIDE; + virtual unsigned int GetBackingFrameBufferObject() OVERRIDE; + + protected: + // ImageTransportSurface implementation + virtual void OnBuffersSwappedACK() OVERRIDE; + virtual void OnNewSurfaceACK(uint64 surface_id, + TransportDIB::Handle shm_handle) OVERRIDE; + virtual void OnResize(gfx::Size size) OVERRIDE; + + private: + virtual ~TransportDIBImageTransportSurface() OVERRIDE; + + uint32 fbo_id_; + GLuint render_buffer_id_; + + scoped_ptr<TransportDIB> shared_mem_; + + gfx::Size size_; + + static uint32 next_id_; + + scoped_ptr<ImageTransportHelper> helper_; + + DISALLOW_COPY_AND_ASSIGN(TransportDIBImageTransportSurface); +}; + +uint32 TransportDIBImageTransportSurface::next_id_ = 1; + +void AddBooleanValue(CFMutableDictionaryRef dictionary, + const CFStringRef key, + bool value) { + CFDictionaryAddValue(dictionary, key, + (value ? kCFBooleanTrue : kCFBooleanFalse)); +} + +void AddIntegerValue(CFMutableDictionaryRef dictionary, + const CFStringRef key, + int32 value) { + base::mac::ScopedCFTypeRef<CFNumberRef> number( + CFNumberCreate(NULL, kCFNumberSInt32Type, &value)); + CFDictionaryAddValue(dictionary, key, number.get()); +} + +IOSurfaceImageTransportSurface::IOSurfaceImageTransportSurface( + GpuChannelManager* manager, + int32 render_view_id, + int32 renderer_id, + int32 command_buffer_id, + gfx::PluginWindowHandle handle) + : gfx::PbufferGLSurfaceCGL(gfx::Size(1, 1)), + fbo_id_(0), + texture_id_(0), + io_surface_id_(0), + context_(NULL) { + helper_.reset(new ImageTransportHelper(this, + manager, + render_view_id, + renderer_id, + command_buffer_id, + handle)); + +} + +IOSurfaceImageTransportSurface::~IOSurfaceImageTransportSurface() { + Destroy(); +} + +bool IOSurfaceImageTransportSurface::Initialize() { + // Only support IOSurfaces if the GL implementation is the native desktop GL. + // IO surfaces will not work with, for example, OSMesa software renderer + // GL contexts. + if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL) + return false; + + if (!helper_->Initialize()) + return false; + return PbufferGLSurfaceCGL::Initialize(); +} + +void IOSurfaceImageTransportSurface::Destroy() { + if (fbo_id_) { + glDeleteFramebuffersEXT(1, &fbo_id_); + fbo_id_ = 0; + } + + if (texture_id_) { + glDeleteTextures(1, &texture_id_); + texture_id_ = 0; + } + + helper_->Destroy(); + PbufferGLSurfaceCGL::Destroy(); +} + +bool IOSurfaceImageTransportSurface::IsOffscreen() { + return false; +} + +void IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) { + context_ = context; + + if (fbo_id_) + return; + + glGenFramebuffersEXT(1, &fbo_id_); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); + OnResize(gfx::Size(1, 1)); + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LOG(ERROR) << "Framebuffer incomplete."; + } +} + +unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() { + return fbo_id_; +} + +bool IOSurfaceImageTransportSurface::SwapBuffers() { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); + glFlush(); + + GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; + params.surface_id = io_surface_id_; + helper_->SendAcceleratedSurfaceBuffersSwapped(params); + + helper_->SetScheduled(false); + return true; +} + +gfx::Size IOSurfaceImageTransportSurface::GetSize() { + return size_; +} + +void IOSurfaceImageTransportSurface::OnBuffersSwappedACK() { + helper_->SetScheduled(true); +} + +void IOSurfaceImageTransportSurface::OnNewSurfaceACK( + uint64 surface_id, + TransportDIB::Handle /* shm_handle */) { + DCHECK_EQ(io_surface_id_, surface_id); + helper_->SetScheduled(true); +} + +void IOSurfaceImageTransportSurface::OnResize(gfx::Size size) { + IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); + + // Caching |context_| from OnMakeCurrent. It should still be current. + DCHECK(context_->IsCurrent(this)); + + size_ = size; + + if (texture_id_) + glDeleteTextures(1, &texture_id_); + + glGenTextures(1, &texture_id_); + + // 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; + glBindTexture(target, texture_id_); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + target, + texture_id_, + 0); + + // Allocate a new IOSurface, which is the GPU resource that can be + // shared across processes. + base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> properties; + properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + AddIntegerValue(properties, + io_surface_support->GetKIOSurfaceWidth(), + size_.width()); + AddIntegerValue(properties, + io_surface_support->GetKIOSurfaceHeight(), + size_.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( + static_cast<CGLContextObj>(context_->GetHandle()), + target, + GL_RGBA, + size_.width(), + size_.height(), + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, + io_surface_.get(), + plane); + + io_surface_id_ = io_surface_support->IOSurfaceGetID(io_surface_); + glFlush(); + + GpuHostMsg_AcceleratedSurfaceNew_Params params; + params.width = size_.width(); + params.height = size_.height(); + params.surface_id = io_surface_id_; + params.create_transport_dib = false; + helper_->SendAcceleratedSurfaceNew(params); + + helper_->SetScheduled(false); +} + +TransportDIBImageTransportSurface::TransportDIBImageTransportSurface( + GpuChannelManager* manager, + int32 render_view_id, + int32 renderer_id, + int32 command_buffer_id, + gfx::PluginWindowHandle handle) + : gfx::PbufferGLSurfaceCGL(gfx::Size(1, 1)), + fbo_id_(0), + render_buffer_id_(0) { + helper_.reset(new ImageTransportHelper(this, + manager, + render_view_id, + renderer_id, + command_buffer_id, + handle)); + +} + +TransportDIBImageTransportSurface::~TransportDIBImageTransportSurface() { + Destroy(); +} + +bool TransportDIBImageTransportSurface::Initialize() { + if (!helper_->Initialize()) + return false; + return PbufferGLSurfaceCGL::Initialize(); +} + +void TransportDIBImageTransportSurface::Destroy() { + if (fbo_id_) { + glDeleteFramebuffersEXT(1, &fbo_id_); + fbo_id_ = 0; + } + + if (render_buffer_id_) { + glDeleteRenderbuffersEXT(1, &render_buffer_id_); + render_buffer_id_ = 0; + } + + helper_->Destroy(); + PbufferGLSurfaceCGL::Destroy(); +} + +bool TransportDIBImageTransportSurface::IsOffscreen() { + return false; +} + +void TransportDIBImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) { + if (fbo_id_) + return; + + glGenFramebuffersEXT(1, &fbo_id_); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); + OnResize(gfx::Size(1, 1)); + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LOG(ERROR) << "Framebuffer incomplete."; + } +} + +unsigned int TransportDIBImageTransportSurface::GetBackingFrameBufferObject() { + return fbo_id_; +} + +bool TransportDIBImageTransportSurface::SwapBuffers() { + DCHECK_NE(shared_mem_.get(), static_cast<void*>(NULL)); + + GLint current_alignment; + glGetIntegerv(GL_PACK_ALIGNMENT, ¤t_alignment); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glReadPixels(0, 0, + size_.width(), size_.height(), + GL_BGRA, // This pixel format should have no conversion. + GL_UNSIGNED_INT_8_8_8_8_REV, + shared_mem_->memory()); + glPixelStorei(GL_PACK_ALIGNMENT, current_alignment); + + GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; + params.surface_id = next_id_; + helper_->SendAcceleratedSurfaceBuffersSwapped(params); + + helper_->SetScheduled(false); + return true; +} + +gfx::Size TransportDIBImageTransportSurface::GetSize() { + return size_; +} + +void TransportDIBImageTransportSurface::OnBuffersSwappedACK() { + helper_->SetScheduled(true); +} + +void TransportDIBImageTransportSurface::OnNewSurfaceACK( + uint64 surface_id, + TransportDIB::Handle shm_handle) { + helper_->SetScheduled(true); + + shared_mem_.reset(TransportDIB::Map(shm_handle)); + DCHECK_NE(shared_mem_.get(), static_cast<void*>(NULL)); +} + +void TransportDIBImageTransportSurface::OnResize(gfx::Size size) { + size_ = size; + + if (!render_buffer_id_) + glGenRenderbuffersEXT(1, &render_buffer_id_); + + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buffer_id_); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, + GL_RGBA, + size_.width(), size_.height()); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_RENDERBUFFER_EXT, + render_buffer_id_); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + GpuHostMsg_AcceleratedSurfaceNew_Params params; + params.width = size_.width(); + params.height = size_.height(); + params.surface_id = next_id_++; + params.create_transport_dib = true; + helper_->SendAcceleratedSurfaceNew(params); + + helper_->SetScheduled(false); +} + +} // namespace + +// static +scoped_refptr<gfx::GLSurface> ImageTransportSurface::CreateSurface( + GpuChannelManager* manager, + int32 render_view_id, + int32 renderer_id, + int32 command_buffer_id, + gfx::PluginWindowHandle handle) { + scoped_refptr<gfx::GLSurface> surface; + IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); + + switch (gfx::GetGLImplementation()) { + case gfx::kGLImplementationDesktopGL: + if (!io_surface_support) { + surface = new TransportDIBImageTransportSurface(manager, + render_view_id, + renderer_id, + command_buffer_id, + handle); + } else { + surface = new IOSurfaceImageTransportSurface(manager, + render_view_id, + renderer_id, + command_buffer_id, + handle); + } + break; + default: + NOTREACHED(); + return NULL; + } + if (surface->Initialize()) + return surface; + else + return NULL; +} + +#endif // defined(USE_GPU) |
