diff options
author | backer@chromium.org <backer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-25 14:04:12 +0000 |
---|---|---|
committer | backer@chromium.org <backer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-25 14:04:12 +0000 |
commit | b9b751f2681d39b11a692a655eb16a9e1e4eba97 (patch) | |
tree | ef152fd04b0a9730f9088aa1b968e6096097127e /ui | |
parent | 13ffb98bc729794380228ebdc09b18687d2d1d74 (diff) | |
download | chromium_src-b9b751f2681d39b11a692a655eb16a9e1e4eba97.zip chromium_src-b9b751f2681d39b11a692a655eb16a9e1e4eba97.tar.gz chromium_src-b9b751f2681d39b11a692a655eb16a9e1e4eba97.tar.bz2 |
Refactor: Move app/surface to ui/gfx/surface.
This is progress towards moving app/gfx/gl to ui/gfx/gl. Currently, surface depends on gl, but gl depends on app_base (which contains surface). This refactor gets rid of this potential cyclic dependency.
surface.gyp is in a separate file (not a .gypi include) because cyclic dependency checking is at the file level (not target level).
BUG=none
TEST=by hand and try bots
Review URL: http://codereview.chromium.org/6718027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79396 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/gfx/surface/accelerated_surface_mac.cc | 347 | ||||
-rw-r--r-- | ui/gfx/surface/accelerated_surface_mac.h | 167 | ||||
-rw-r--r-- | ui/gfx/surface/io_surface_support_mac.cc | 270 | ||||
-rw-r--r-- | ui/gfx/surface/io_surface_support_mac.h | 70 | ||||
-rw-r--r-- | ui/gfx/surface/surface.gyp | 57 | ||||
-rw-r--r-- | ui/gfx/surface/transport_dib.h | 202 | ||||
-rw-r--r-- | ui/gfx/surface/transport_dib_linux.cc | 141 | ||||
-rw-r--r-- | ui/gfx/surface/transport_dib_mac.cc | 98 | ||||
-rw-r--r-- | ui/gfx/surface/transport_dib_win.cc | 110 |
9 files changed, 1462 insertions, 0 deletions
diff --git a/ui/gfx/surface/accelerated_surface_mac.cc b/ui/gfx/surface/accelerated_surface_mac.cc new file mode 100644 index 0000000..59919f8 --- /dev/null +++ b/ui/gfx/surface/accelerated_surface_mac.cc @@ -0,0 +1,347 @@ +// 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. + +#include "ui/gfx/surface/accelerated_surface_mac.h" + +#include "app/gfx/gl/gl_bindings.h" +#include "app/gfx/gl/gl_implementation.h" +#include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/surface/io_surface_support_mac.h" + +AcceleratedSurface::AcceleratedSurface() + : io_surface_id_(0), + allocate_fbo_(false), + texture_(0), + fbo_(0), + depth_stencil_renderbuffer_(0) { +} + +AcceleratedSurface::~AcceleratedSurface() {} + +bool AcceleratedSurface::Initialize(gfx::GLContext* share_context, + bool allocate_fbo) { + allocate_fbo_ = allocate_fbo; + + // Ensure GL is initialized before trying to create an offscreen GL context. + if (!gfx::GLContext::InitializeOneOff()) + return false; + + // Drawing to IOSurfaces via OpenGL only works with desktop GL and + // not with the OSMesa software renderer. + if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL) + return false; + + gl_context_.reset(gfx::GLContext::CreateOffscreenGLContext(share_context)); + if (!gl_context_.get()) + return false; + + // Now we're ready to handle SetSurfaceSize calls, which will + // allocate and/or reallocate the IOSurface and associated offscreen + // OpenGL structures for rendering. + return true; +} + +void AcceleratedSurface::Destroy() { + // The FBO and texture objects will be destroyed when the OpenGL context, + // and any other contexts sharing resources with it, is. We don't want to + // make the context current one last time here just in order to delete + // these objects. + + // 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_.get()) + gl_context_->Destroy(); + gl_context_.reset(); +} + +// Call after making changes to the surface which require a visual update. +// Makes the rendering show up in other processes. +void AcceleratedSurface::SwapBuffers() { + if (io_surface_.get() != NULL) { + if (allocate_fbo_) { + // 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 { + // Copy the current framebuffer's contents into our "live" texture. + // Note that the current GL context might not be ours at this point! + // This is deliberate, so that surrounding code using GL can produce + // rendering results consumed by the AcceleratedSurface. + // Need to save and restore OpenGL state around this call. + GLint current_texture = 0; + GLenum target_binding = GL_TEXTURE_BINDING_RECTANGLE_ARB; + GLenum target = GL_TEXTURE_RECTANGLE_ARB; + glGetIntegerv(target_binding, ¤t_texture); + glBindTexture(target, texture_); + glCopyTexSubImage2D(target, 0, + 0, 0, + 0, 0, + surface_size_.width(), surface_size_.height()); + glBindTexture(target, current_texture); + } + } else if (transport_dib_.get() != NULL) { + // Pre-Mac OS X 10.6, fetch the rendered image from the current frame + // buffer 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(). + glReadPixels(0, + 0, + surface_size_.width(), + surface_size_.height(), + GL_BGRA, // This pixel format should have no conversion. + GL_UNSIGNED_INT_8_8_8_8_REV, + pixel_memory); + } + } +} + +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) { + base::mac::ScopedCFTypeRef<CFNumberRef> number( + CFNumberCreate(NULL, kCFNumberSInt32Type, &value)); + CFDictionaryAddValue(dictionary, key, number.get()); +} + +void AcceleratedSurface::AllocateRenderBuffers(GLenum target, + const gfx::Size& size) { + 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); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // Generate and bind the framebuffer object. + glGenFramebuffersEXT(1, &fbo_); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 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, + size.width(), + size.height()); + + // Unbind the renderbuffers. + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + // Make sure that subsequent set-up code affects the render texture. + glBindTexture(target, texture_); +} + +bool AcceleratedSurface::SetupFrameBufferObject(GLenum target) { + 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); + } + return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT; +} + +bool AcceleratedSurface::MakeCurrent() { + if (!gl_context_.get()) + return false; + return gl_context_->MakeCurrent(); +} + +void AcceleratedSurface::Clear(const gfx::Rect& rect) { + DCHECK(gl_context_->IsCurrent()); + glClearColor(0, 0, 0, 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(const gfx::Size& size) { + if (surface_size_ == size) { + // Return 0 to indicate to the caller that no new backing store + // allocation occurred. + return 0; + } + + // Only support IO surfaces 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 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; + if (allocate_fbo_) { + AllocateRenderBuffers(target, size); + } else 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); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + // 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>(gl_context_->GetHandle()), + target, + GL_RGBA, + size.width(), + size.height(), + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, + io_surface_.get(), + plane); + if (allocate_fbo_) { + // Set up the frame buffer object. + SetupFrameBufferObject(target); + } + surface_size_ = size; + + // 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. + io_surface_id_ = io_surface_support->IOSurfaceGetID(io_surface_); + return io_surface_id_; +} + +uint64 AcceleratedSurface::GetSurfaceId() { + return io_surface_id_; +} + +TransportDIB::Handle AcceleratedSurface::SetTransportDIBSize( + const gfx::Size& size) { + if (surface_size_ == size) { + // Return an invalid handle to indicate to the caller that no new backing + // store allocation occurred. + return TransportDIB::DefaultHandleValue(); + } + surface_size_ = size; + + // 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 = size.width() * 4 * size.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_handle(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(); + } + + if (allocate_fbo_) { + DCHECK(gl_context_->IsCurrent()); + // Set up the render buffers and reserve enough space on the card for the + // framebuffer texture. + GLenum target = GL_TEXTURE_RECTANGLE_ARB; + AllocateRenderBuffers(target, size); + glTexImage2D(target, + 0, // mipmap level 0 + GL_RGBA8, // internal pixel format + size.width(), + size.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/ui/gfx/surface/accelerated_surface_mac.h b/ui/gfx/surface/accelerated_surface_mac.h new file mode 100644 index 0000000..44ce8d5 --- /dev/null +++ b/ui/gfx/surface/accelerated_surface_mac.h @@ -0,0 +1,167 @@ +// 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. + +#ifndef UI_GFX_SURFACE_ACCELERATED_SURFACE_MAC_H_ +#define UI_GFX_SURFACE_ACCELERATED_SURFACE_MAC_H_ +#pragma once + +#include <CoreFoundation/CoreFoundation.h> + +#include "app/gfx/gl/gl_context.h" +#include "base/callback.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/scoped_ptr.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" +#include "ui/gfx/surface/transport_dib.h" + +// Should not include GL headers in a header file. Forward declare these types +// instead. +typedef struct _CGLContextObject* CGLContextObj; +typedef unsigned int GLenum; +typedef unsigned int GLuint; + +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 an IOSurface or a TransportDIB, +// never both. + +class AcceleratedSurface { + public: + AcceleratedSurface(); + virtual ~AcceleratedSurface(); + + // Set up internal buffers. |share_context|, if non-NULL, is a context + // with which the internally created OpenGL context shares textures and + // other resources. |allocate_fbo| indicates whether or not this surface + // should allocate an offscreen frame buffer object (FBO) internally. If + // not, then the user is expected to allocate one. NOTE that allocating + // an FBO internally does NOT work properly with client code which uses + // OpenGL (i.e., via GLES2 command buffers), because the GLES2 + // implementation does not know to bind the accelerated surface's + // internal FBO when the default FBO is bound. Returns false upon + // failure. + bool Initialize(gfx::GLContext* share_context, bool allocate_fbo); + // Tear down. Must be called before destructor to prevent leaks. + void Destroy(); + + // These methods are used only once the accelerated surface is initialized. + + // 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(const gfx::Size& size); + + // Returns the id of this surface's IOSruface, or 0 for + // transport DIB surfaces. + uint64 GetSurfaceId(); + + // Sets the GL context to be the current one for drawing. Returns true if + // it succeeded. + bool MakeCurrent(); + // Clear the surface to be transparent. 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. Assumes the caller has + // already called MakeCurrent(). + // + // If this AcceleratedSurface is configured with its own FBO, then + // this call causes the color buffer to be transmitted. Otherwise, + // it causes the frame buffer of the current GL context to be copied + // either into an internal texture via glCopyTexSubImage2D or into a + // TransportDIB via glReadPixels. + // + // The size of the rectangle copied is the size last specified via + // SetSurfaceSize. If another GL context than the one this + // AcceleratedSurface contains is responsible for the production of + // the pixels, then when this entry point is called, the color + // buffer must be in a state where a glCopyTexSubImage2D or + // glReadPixels is legal. (For example, if using multisampled FBOs, + // the FBO must have been resolved into a non-multisampled color + // texture.) Additionally, in this situation, the contexts must + // share server-side GL objects, so that this AcceleratedSurface's + // texture is a legal name in the namespace of the current context. + void SwapBuffers(); + + CGLContextObj context() { + return static_cast<CGLContextObj>(gl_context_->GetHandle()); + } + + // 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. Assumes the caller has already called + // MakeCurrent(). + TransportDIB::Handle SetTransportDIBSize(const gfx::Size& size); + // 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); + + // Get the accelerated surface size. + gfx::Size GetSize() const { return surface_size_; } + + 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, const gfx::Size& size); + + // 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); + + // The OpenGL context, and pbuffer drawable, used to transfer data + // to the shared region (IOSurface or TransportDIB). Strictly + // speaking, we do not need to allocate a GL context all of the + // time. We only need one if (a) we are using the IOSurface code + // path, or (b) if we are allocating an FBO internally. + scoped_ptr<gfx::GLContext> gl_context_; + // 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? + base::mac::ScopedCFTypeRef<CFTypeRef> io_surface_; + + // The id of |io_surface_| or 0 if that's NULL. + uint64 io_surface_id_; + + // 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_; + gfx::Size surface_size_; + // TODO(kbr): the FBO management should not be in this class at all. + // However, if it is factored out, care needs to be taken to not + // introduce another copy of the color data on the GPU; the direct + // binding of the internal texture to the IOSurface saves a copy. + bool allocate_fbo_; + // If the IOSurface code path is being used, then this texture + // object is always allocated. Otherwise, it is only allocated if + // the user requests an FBO be allocated. + GLuint texture_; + // The FBO and renderbuffer are only allocated if allocate_fbo_ is + // true. + GLuint fbo_; + GLuint depth_stencil_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 // UI_GFX_SURFACE_ACCELERATED_SURFACE_MAC_H_ diff --git a/ui/gfx/surface/io_surface_support_mac.cc b/ui/gfx/surface/io_surface_support_mac.cc new file mode 100644 index 0000000..15e33dc --- /dev/null +++ b/ui/gfx/surface/io_surface_support_mac.cc @@ -0,0 +1,270 @@ +// 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. + +#include <dlfcn.h> + +#include "base/singleton.h" +#include "ui/gfx/surface/io_surface_support_mac.h" + +typedef CFTypeRef (*IOSurfaceCreateProcPtr)(CFDictionaryRef properties); +typedef uint32 (*IOSurfaceGetIDProcPtr)(CFTypeRef io_surface); +typedef CFTypeRef (*IOSurfaceLookupProcPtr)(uint32 io_surface_id); +typedef mach_port_t (*IOSurfaceCreateMachPortProcPtr)(CFTypeRef io_surface); +typedef CFTypeRef (*IOSurfaceLookupFromMachPortProcPtr)(mach_port_t port); +typedef size_t (*IOSurfaceGetWidthPtr)(CFTypeRef io_surface); +typedef size_t (*IOSurfaceGetHeightPtr)(CFTypeRef io_surface); +typedef CGLError (*CGLTexImageIOSurface2DProcPtr)(CGLContextObj ctx, + GLenum target, + GLenum internal_format, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + CFTypeRef io_surface, + GLuint plane); + +class IOSurfaceSupportImpl : public IOSurfaceSupport { + public: + static IOSurfaceSupportImpl* GetInstance(); + + bool InitializedSuccessfully() { + return initialized_successfully_; + } + + virtual CFStringRef GetKIOSurfaceWidth(); + virtual CFStringRef GetKIOSurfaceHeight(); + virtual CFStringRef GetKIOSurfaceBytesPerElement(); + virtual CFStringRef GetKIOSurfaceIsGlobal(); + + virtual CFTypeRef IOSurfaceCreate(CFDictionaryRef properties); + virtual uint32 IOSurfaceGetID(CFTypeRef io_surface); + virtual CFTypeRef IOSurfaceLookup(uint32 io_surface_id); + virtual mach_port_t IOSurfaceCreateMachPort(CFTypeRef io_surface); + virtual CFTypeRef IOSurfaceLookupFromMachPort(mach_port_t port); + + virtual size_t IOSurfaceGetWidth(CFTypeRef io_surface); + virtual size_t IOSurfaceGetHeight(CFTypeRef io_surface); + + virtual CGLError CGLTexImageIOSurface2D(CGLContextObj ctx, + GLenum target, + GLenum internal_format, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + CFTypeRef io_surface, + GLuint plane); + + private: + IOSurfaceSupportImpl(); + ~IOSurfaceSupportImpl(); + + void* iosurface_handle_; + void* opengl_handle_; + CFStringRef k_io_surface_width_; + CFStringRef k_io_surface_height_; + CFStringRef k_io_surface_bytes_per_element_; + CFStringRef k_io_surface_is_global_; + IOSurfaceCreateProcPtr io_surface_create_; + IOSurfaceGetIDProcPtr io_surface_get_id_; + IOSurfaceLookupProcPtr io_surface_lookup_; + IOSurfaceCreateMachPortProcPtr io_surface_create_mach_port_; + IOSurfaceLookupFromMachPortProcPtr io_surface_lookup_from_mach_port_; + IOSurfaceGetWidthPtr io_surface_get_width_; + IOSurfaceGetHeightPtr io_surface_get_height_; + CGLTexImageIOSurface2DProcPtr cgl_tex_image_io_surface_2d_; + bool initialized_successfully_; + + friend struct DefaultSingletonTraits<IOSurfaceSupportImpl>; + DISALLOW_COPY_AND_ASSIGN(IOSurfaceSupportImpl); +}; + +IOSurfaceSupportImpl* IOSurfaceSupportImpl::GetInstance() { + IOSurfaceSupportImpl* impl = Singleton<IOSurfaceSupportImpl>::get(); + if (impl->InitializedSuccessfully()) + return impl; + return NULL; +} + +CFStringRef IOSurfaceSupportImpl::GetKIOSurfaceWidth() { + return k_io_surface_width_; +} + +CFStringRef IOSurfaceSupportImpl::GetKIOSurfaceHeight() { + return k_io_surface_height_; +} + +CFStringRef IOSurfaceSupportImpl::GetKIOSurfaceBytesPerElement() { + return k_io_surface_bytes_per_element_; +} + +CFStringRef IOSurfaceSupportImpl::GetKIOSurfaceIsGlobal() { + return k_io_surface_is_global_; +} + +CFTypeRef IOSurfaceSupportImpl::IOSurfaceCreate(CFDictionaryRef properties) { + return io_surface_create_(properties); +} + +uint32 IOSurfaceSupportImpl::IOSurfaceGetID( + CFTypeRef io_surface) { + return io_surface_get_id_(io_surface); +} + +CFTypeRef IOSurfaceSupportImpl::IOSurfaceLookup(uint32 io_surface_id) { + return io_surface_lookup_(io_surface_id); +} + +mach_port_t IOSurfaceSupportImpl::IOSurfaceCreateMachPort( + CFTypeRef io_surface) { + return io_surface_create_mach_port_(io_surface); +} + +CFTypeRef IOSurfaceSupportImpl::IOSurfaceLookupFromMachPort(mach_port_t port) { + return io_surface_lookup_from_mach_port_(port); +} + +size_t IOSurfaceSupportImpl::IOSurfaceGetWidth(CFTypeRef io_surface) { + return io_surface_get_width_(io_surface); +} + +size_t IOSurfaceSupportImpl::IOSurfaceGetHeight(CFTypeRef io_surface) { + return io_surface_get_height_(io_surface); +} + + +CGLError IOSurfaceSupportImpl::CGLTexImageIOSurface2D(CGLContextObj ctx, + GLenum target, + GLenum internal_format, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + CFTypeRef io_surface, + GLuint plane) { + return cgl_tex_image_io_surface_2d_(ctx, + target, + internal_format, + width, + height, + format, + type, + io_surface, + plane); +} + +IOSurfaceSupportImpl::IOSurfaceSupportImpl() + : iosurface_handle_(NULL), + opengl_handle_(NULL), + k_io_surface_width_(NULL), + k_io_surface_height_(NULL), + k_io_surface_bytes_per_element_(NULL), + k_io_surface_is_global_(NULL), + io_surface_create_(NULL), + io_surface_get_id_(NULL), + io_surface_lookup_(NULL), + io_surface_create_mach_port_(NULL), + io_surface_lookup_from_mach_port_(NULL), + io_surface_get_width_(NULL), + io_surface_get_height_(NULL), + cgl_tex_image_io_surface_2d_(NULL), + initialized_successfully_(false) { + iosurface_handle_ = dlopen( + "/System/Library/Frameworks/IOSurface.framework/IOSurface", + RTLD_LAZY | RTLD_LOCAL); + if (!iosurface_handle_) + return; + opengl_handle_ = dlopen( + "/System/Library/Frameworks/OpenGL.framework/OpenGL", + RTLD_LAZY | RTLD_LOCAL); + if (!opengl_handle_) { + dlclose(iosurface_handle_); + iosurface_handle_ = NULL; + return; + } + + void* surface_width_ptr = dlsym(iosurface_handle_, "kIOSurfaceWidth"); + void* surface_height_ptr = dlsym(iosurface_handle_, "kIOSurfaceHeight"); + void* surface_bytes_per_element_ptr = + dlsym(iosurface_handle_, "kIOSurfaceBytesPerElement"); + void* surface_is_global_ptr = + dlsym(iosurface_handle_, "kIOSurfaceIsGlobal"); + void* surface_create_ptr = dlsym(iosurface_handle_, "IOSurfaceCreate"); + void* surface_get_id_ptr = dlsym(iosurface_handle_, "IOSurfaceGetID"); + void* surface_lookup_ptr = dlsym(iosurface_handle_, "IOSurfaceLookup"); + void* surface_create_mach_port_ptr = + dlsym(iosurface_handle_, "IOSurfaceCreateMachPort"); + void* surface_lookup_from_mach_port_ptr = + dlsym(iosurface_handle_, "IOSurfaceLookupFromMachPort"); + void* io_surface_get_width_ptr = + dlsym(iosurface_handle_, "IOSurfaceGetWidth"); + void* io_surface_get_height_ptr = + dlsym(iosurface_handle_, "IOSurfaceGetHeight"); + void* tex_image_io_surface_2d_ptr = + dlsym(opengl_handle_, "CGLTexImageIOSurface2D"); + if (!surface_width_ptr || + !surface_height_ptr || + !surface_bytes_per_element_ptr || + !surface_is_global_ptr || + !surface_create_ptr || + !surface_get_id_ptr || + !surface_lookup_ptr || + !surface_create_mach_port_ptr || + !surface_lookup_from_mach_port_ptr || + !io_surface_get_width_ptr || + !io_surface_get_height_ptr || + !tex_image_io_surface_2d_ptr) { + dlclose(iosurface_handle_); + iosurface_handle_ = NULL; + dlclose(opengl_handle_); + opengl_handle_ = NULL; + return; + } + + k_io_surface_width_ = *static_cast<CFStringRef*>(surface_width_ptr); + k_io_surface_height_ = *static_cast<CFStringRef*>(surface_height_ptr); + k_io_surface_bytes_per_element_ = + *static_cast<CFStringRef*>(surface_bytes_per_element_ptr); + k_io_surface_is_global_ = *static_cast<CFStringRef*>(surface_is_global_ptr); + io_surface_create_ = reinterpret_cast<IOSurfaceCreateProcPtr>( + surface_create_ptr); + io_surface_get_id_ = + reinterpret_cast<IOSurfaceGetIDProcPtr>(surface_get_id_ptr); + io_surface_lookup_ = + reinterpret_cast<IOSurfaceLookupProcPtr>(surface_lookup_ptr); + io_surface_create_mach_port_ = + reinterpret_cast<IOSurfaceCreateMachPortProcPtr>( + surface_create_mach_port_ptr); + io_surface_lookup_from_mach_port_ = + reinterpret_cast<IOSurfaceLookupFromMachPortProcPtr>( + surface_lookup_from_mach_port_ptr); + io_surface_get_width_ = + reinterpret_cast<IOSurfaceGetWidthPtr>( + io_surface_get_width_ptr); + io_surface_get_height_ = + reinterpret_cast<IOSurfaceGetHeightPtr>( + io_surface_get_height_ptr); + cgl_tex_image_io_surface_2d_ = + reinterpret_cast<CGLTexImageIOSurface2DProcPtr>( + tex_image_io_surface_2d_ptr); + initialized_successfully_ = true; +} + +IOSurfaceSupportImpl::~IOSurfaceSupportImpl() { + if (iosurface_handle_) + dlclose(iosurface_handle_); + if (opengl_handle_) + dlclose(opengl_handle_); +} + +IOSurfaceSupport* IOSurfaceSupport::Initialize() { + return IOSurfaceSupportImpl::GetInstance(); +} + +IOSurfaceSupport::IOSurfaceSupport() { +} + +IOSurfaceSupport::~IOSurfaceSupport() { +} + diff --git a/ui/gfx/surface/io_surface_support_mac.h b/ui/gfx/surface/io_surface_support_mac.h new file mode 100644 index 0000000..e8d01b9 --- /dev/null +++ b/ui/gfx/surface/io_surface_support_mac.h @@ -0,0 +1,70 @@ +// 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 UI_GFX_SURFACE_IO_SURFACE_SUPPORT_MAC_H_ +#define UI_GFX_SURFACE_IO_SURFACE_SUPPORT_MAC_H_ +#pragma once + +#include <CoreFoundation/CoreFoundation.h> +#include <mach/mach.h> +#include <OpenGL/OpenGL.h> + +#include "base/basictypes.h" + +// This Mac OS X-specific class provides dynamically-linked access to +// IOSurface.framework, which is only available on 10.6 and later. +// Since Chromium is built on 10.5 we must dynamically look up all of +// the entry points we need in this framework. + +// See IOSurface/IOSurfaceAPI.h and OpenGL/CGLIOSurface.h on 10.6 for +// documentation of the fields and methods of this class. + +class IOSurfaceSupport { + public: + // Returns an instance of the IOSurfaceSupport class if the + // operating system supports it, NULL otherwise. It is safe to call + // this multiple times. + static IOSurfaceSupport* Initialize(); + + virtual CFStringRef GetKIOSurfaceWidth() = 0; + virtual CFStringRef GetKIOSurfaceHeight() = 0; + virtual CFStringRef GetKIOSurfaceBytesPerElement() = 0; + virtual CFStringRef GetKIOSurfaceIsGlobal() = 0; + + virtual CFTypeRef IOSurfaceCreate(CFDictionaryRef properties) = 0; + + // The following two APIs assume the IOSurface was created with the + // kIOSurfaceIsGlobal key set to true + virtual uint32 IOSurfaceGetID(CFTypeRef io_surface) = 0; + virtual CFTypeRef IOSurfaceLookup(uint32 io_surface_id) = 0; + + // The following two APIs are more robust and secure, but + // unfortunately it looks like it will be a lot of work to correctly + // transmit a mach port from process to process (possibly requiring + // a side channel for or extension of the Chrome IPC mechanism) + virtual mach_port_t IOSurfaceCreateMachPort(CFTypeRef io_surface) = 0; + virtual CFTypeRef IOSurfaceLookupFromMachPort(mach_port_t port) = 0; + + virtual size_t IOSurfaceGetWidth(CFTypeRef io_surface) = 0; + virtual size_t IOSurfaceGetHeight(CFTypeRef io_surface) = 0; + + virtual CGLError CGLTexImageIOSurface2D(CGLContextObj ctx, + GLenum target, + GLenum internal_format, + GLsizei width, + GLsizei height, + GLenum format, + GLenum type, + CFTypeRef io_surface, + GLuint plane) = 0; + + protected: + IOSurfaceSupport(); + virtual ~IOSurfaceSupport(); + + DISALLOW_COPY_AND_ASSIGN(IOSurfaceSupport); +}; + +#endif // UI_GFX_SURFACE_IO_SURFACE_SUPPORT_MAC_H_ + diff --git a/ui/gfx/surface/surface.gyp b/ui/gfx/surface/surface.gyp new file mode 100644 index 0000000..c7d1dca --- /dev/null +++ b/ui/gfx/surface/surface.gyp @@ -0,0 +1,57 @@ +# 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. + +{ + 'target_defaults': { + 'sources/': [ + ['exclude', '/(cocoa|gtk|win)/'], + ['exclude', '_(cocoa|gtk|linux|mac|posix|win|x)\\.(cc|mm?)$'], + ['exclude', '/(gtk|win|x11)_[^/]*\\.cc$'], + ], + 'conditions': [ + ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', {'sources/': [ + ['include', '/gtk/'], + ['include', '_(gtk|linux|posix|skia|x)\\.cc$'], + ['include', '/(gtk|x11)_[^/]*\\.cc$'], + ]}], + ['OS=="mac"', {'sources/': [ + ['include', '/cocoa/'], + ['include', '_(cocoa|mac|posix)\\.(cc|mm?)$'], + ]}, { # else: OS != "mac" + 'sources/': [ + ['exclude', '\\.mm?$'], + ], + }], + ['OS=="win"', + {'sources/': [ + ['include', '_(win)\\.cc$'], + ['include', '/win/'], + ['include', '/win_[^/]*\\.cc$'], + ]}], + ], + }, + 'targets': [ + { + 'target_name': 'surface', + 'type': '<(library)', + 'msvs_guid': '6C99567D-6F02-43B5-BB35-D8E3F8D0D6D2', + 'dependencies': [ + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/app/app.gyp:app_base', + '<(DEPTH)/skia/skia.gyp:skia', + '<(DEPTH)/ui/ui.gyp:ui_gfx', + ], + 'sources': [ + 'accelerated_surface_mac.cc', + 'accelerated_surface_mac.h', + 'io_surface_support_mac.cc', + 'io_surface_support_mac.h', + 'transport_dib.h', + 'transport_dib_linux.cc', + 'transport_dib_mac.cc', + 'transport_dib_win.cc', + ], + }, + ], +} diff --git a/ui/gfx/surface/transport_dib.h b/ui/gfx/surface/transport_dib.h new file mode 100644 index 0000000..1834eec --- /dev/null +++ b/ui/gfx/surface/transport_dib.h @@ -0,0 +1,202 @@ +// 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 UI_GFX_SURFACE_TRANSPORT_DIB_H_ +#define UI_GFX_SURFACE_TRANSPORT_DIB_H_ +#pragma once + +#include "base/basictypes.h" + +#if defined(OS_WIN) || defined(OS_MACOSX) +#include "base/shared_memory.h" +#endif + +#if defined(OS_WIN) +#include <windows.h> +#elif defined(USE_X11) +#include "ui/base/x/x11_util.h" +#endif + +namespace skia { +class PlatformCanvas; +} + +// ----------------------------------------------------------------------------- +// A TransportDIB is a block of memory that is used to transport pixels +// between processes: from the renderer process to the browser, and +// between renderer and plugin processes. +// ----------------------------------------------------------------------------- +class TransportDIB { + public: + ~TransportDIB(); + + // Two typedefs are defined. A Handle is the type which can be sent over + // the wire so that the remote side can map the transport DIB. The Id typedef + // is sufficient to identify the transport DIB when you know that the remote + // side already may have it mapped. +#if defined(OS_WIN) + typedef HANDLE Handle; + // On Windows, the Id type includes a sequence number (epoch) to solve an ABA + // issue: + // 1) Process A creates a transport DIB with HANDLE=1 and sends to B. + // 2) Process B maps the transport DIB and caches 1 -> DIB. + // 3) Process A closes the transport DIB and creates a new one. The new DIB + // is also assigned HANDLE=1. + // 4) Process A sends the Handle to B, but B incorrectly believes that it + // already has it cached. + struct HandleAndSequenceNum { + HandleAndSequenceNum() + : handle(NULL), + sequence_num(0) { + } + + HandleAndSequenceNum(HANDLE h, uint32 seq_num) + : handle(h), + sequence_num(seq_num) { + } + + bool operator<(const HandleAndSequenceNum& other) const { + // Use the lexicographic order on the tuple <handle, sequence_num>. + if (other.handle != handle) + return other.handle < handle; + return other.sequence_num < sequence_num; + } + + HANDLE handle; + uint32 sequence_num; + }; + typedef HandleAndSequenceNum Id; + + // Returns a default, invalid handle, that is meant to indicate a missing + // Transport DIB. + static Handle DefaultHandleValue() { return NULL; } + + // Returns a value that is ONLY USEFUL FOR TESTS WHERE IT WON'T BE + // ACTUALLY USED AS A REAL HANDLE. + static Handle GetFakeHandleForTest() { + static int fake_handle = 10; + return reinterpret_cast<Handle>(fake_handle++); + } +#elif defined(OS_MACOSX) + typedef base::SharedMemoryHandle Handle; + // On Mac, the inode number of the backing file is used as an id. + typedef base::SharedMemoryId Id; + + // Returns a default, invalid handle, that is meant to indicate a missing + // Transport DIB. + static Handle DefaultHandleValue() { return Handle(); } + + // Returns a value that is ONLY USEFUL FOR TESTS WHERE IT WON'T BE + // ACTUALLY USED AS A REAL HANDLE. + static Handle GetFakeHandleForTest() { + static int fake_handle = 10; + return Handle(fake_handle++, false); + } +#elif defined(USE_X11) + typedef int Handle; // These two ints are SysV IPC shared memory keys + struct Id { + // Ensure that default initialized Ids are invalid. + Id() : shmkey(-1) { + } + + bool operator<(const Id& other) const { + return shmkey < other.shmkey; + } + + int shmkey; + }; + + // Returns a default, invalid handle, that is meant to indicate a missing + // Transport DIB. + static Handle DefaultHandleValue() { return -1; } + + // Returns a value that is ONLY USEFUL FOR TESTS WHERE IT WON'T BE + // ACTUALLY USED AS A REAL HANDLE. + static Handle GetFakeHandleForTest() { + static int fake_handle = 10; + return fake_handle++; + } +#endif + + // Create a new TransportDIB, returning NULL on failure. + // + // The size is the minimum size in bytes of the memory backing the transport + // DIB (we may actually allocate more than that to give us better reuse when + // cached). + // + // The sequence number is used to uniquely identify the transport DIB. It + // should be unique for all transport DIBs ever created in the same + // renderer. + static TransportDIB* Create(size_t size, uint32 sequence_num); + + // Map the referenced transport DIB. The caller owns the returned object. + // Returns NULL on failure. + static TransportDIB* Map(Handle transport_dib); + + // Create a new |TransportDIB| with a handle to the shared memory. This + // always returns a valid pointer. The DIB is not mapped. + static TransportDIB* CreateWithHandle(Handle handle); + + // Returns true if the handle is valid. + static bool is_valid_handle(Handle dib); + + // Returns true if the ID refers to a valid dib. + static bool is_valid_id(Id id); + + // Returns a canvas using the memory of this TransportDIB. The returned + // pointer will be owned by the caller. The bitmap will be of the given size, + // which should fit inside this memory. + // + // On POSIX, this |TransportDIB| will be mapped if not already. On Windows, + // this |TransportDIB| will NOT be mapped and should not be mapped prior, + // because PlatformCanvas will map the file internally. + // + // Will return NULL on allocation failure. This could be because the image + // is too large to map into the current process' address space. + skia::PlatformCanvas* GetPlatformCanvas(int w, int h); + + // Map the DIB into the current process if it is not already. This is used to + // map a DIB that has already been created. Returns true if the DIB is mapped. + bool Map(); + + // Return a pointer to the shared memory. + void* memory() const; + + // Return the maximum size of the shared memory. This is not the amount of + // data which is valid, you have to know that via other means, this is simply + // the maximum amount that /could/ be valid. + size_t size() const { return size_; } + + // Return the identifier which can be used to refer to this shared memory + // on the wire. + Id id() const; + + // Return a handle to the underlying shared memory. This can be sent over the + // wire to give this transport DIB to another process. + Handle handle() const; + +#if defined(USE_X11) + // Map the shared memory into the X server and return an id for the shared + // segment. + XID MapToX(Display* connection); +#endif + + private: + TransportDIB(); +#if defined(OS_WIN) || defined(OS_MACOSX) + explicit TransportDIB(base::SharedMemoryHandle dib); + base::SharedMemory shared_memory_; + uint32 sequence_num_; +#elif defined(USE_X11) + Id key_; // SysV shared memory id + void* address_; // mapped address + XSharedMemoryId x_shm_; // X id for the shared segment + Display* display_; // connection to the X server +#endif + size_t size_; // length, in bytes + + DISALLOW_COPY_AND_ASSIGN(TransportDIB); +}; + +#endif // UI_GFX_SURFACE_TRANSPORT_DIB_H_ diff --git a/ui/gfx/surface/transport_dib_linux.cc b/ui/gfx/surface/transport_dib_linux.cc new file mode 100644 index 0000000..3e31c7b --- /dev/null +++ b/ui/gfx/surface/transport_dib_linux.cc @@ -0,0 +1,141 @@ +// 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. + +#include "ui/gfx/surface/transport_dib.h" + +#include <errno.h> +#include <stdlib.h> +#include <sys/ipc.h> +#include <sys/shm.h> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "skia/ext/platform_canvas.h" +#include "ui/base/x/x11_util.h" +#include "ui/gfx/size.h" + +// The shmat system call uses this as it's invalid return address +static void *const kInvalidAddress = (void*) -1; + +TransportDIB::TransportDIB() + : address_(kInvalidAddress), + x_shm_(0), + display_(NULL), + size_(0) { +} + +TransportDIB::~TransportDIB() { + if (address_ != kInvalidAddress) { + shmdt(address_); + address_ = kInvalidAddress; + } + + if (x_shm_) { + DCHECK(display_); + ui::DetachSharedMemory(display_, x_shm_); + } +} + +// static +TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) { + // We use a mode of 0666 since the X server won't attach to memory which is + // 0600 since it can't know if it (as a root process) is being asked to map + // someone else's private shared memory region. + const int shmkey = shmget(IPC_PRIVATE, size, 0666); + if (shmkey == -1) { + DLOG(ERROR) << "Failed to create SysV shared memory region" + << " errno:" << errno; + return NULL; + } + + void* address = shmat(shmkey, NULL /* desired address */, 0 /* flags */); + // Here we mark the shared memory for deletion. Since we attached it in the + // line above, it doesn't actually get deleted but, if we crash, this means + // that the kernel will automatically clean it up for us. + shmctl(shmkey, IPC_RMID, 0); + if (address == kInvalidAddress) + return NULL; + + TransportDIB* dib = new TransportDIB; + + dib->key_.shmkey = shmkey; + dib->address_ = address; + dib->size_ = size; + return dib; +} + +// static +TransportDIB* TransportDIB::Map(Handle handle) { + scoped_ptr<TransportDIB> dib(CreateWithHandle(handle)); + if (!dib->Map()) + return NULL; + return dib.release(); +} + +// static +TransportDIB* TransportDIB::CreateWithHandle(Handle shmkey) { + TransportDIB* dib = new TransportDIB; + dib->key_.shmkey = shmkey; + return dib; +} + +// static +bool TransportDIB::is_valid_handle(Handle dib) { + return dib >= 0; +} + +// static +bool TransportDIB::is_valid_id(Id id) { + return id.shmkey != -1; +} + +skia::PlatformCanvas* TransportDIB::GetPlatformCanvas(int w, int h) { + if (address_ == kInvalidAddress && !Map()) + return NULL; + scoped_ptr<skia::PlatformCanvas> canvas(new skia::PlatformCanvas); + if (!canvas->initialize(w, h, true, reinterpret_cast<uint8_t*>(memory()))) + return NULL; + return canvas.release(); +} + +bool TransportDIB::Map() { + if (!is_valid_id(key_)) + return false; + if (address_ != kInvalidAddress) + return true; + + struct shmid_ds shmst; + if (shmctl(key_.shmkey, IPC_STAT, &shmst) == -1) + return false; + + void* address = shmat(key_.shmkey, NULL /* desired address */, 0 /* flags */); + if (address == kInvalidAddress) + return false; + + address_ = address; + size_ = shmst.shm_segsz; + return true; +} + +void* TransportDIB::memory() const { + DCHECK_NE(address_, kInvalidAddress); + return address_; +} + +TransportDIB::Id TransportDIB::id() const { + return key_; +} + +TransportDIB::Handle TransportDIB::handle() const { + return key_.shmkey; +} + +XID TransportDIB::MapToX(Display* display) { + if (!x_shm_) { + x_shm_ = ui::AttachSharedMemory(display, key_.shmkey); + display_ = display; + } + + return x_shm_; +} diff --git a/ui/gfx/surface/transport_dib_mac.cc b/ui/gfx/surface/transport_dib_mac.cc new file mode 100644 index 0000000..268bce1 --- /dev/null +++ b/ui/gfx/surface/transport_dib_mac.cc @@ -0,0 +1,98 @@ +// 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. + +#include "ui/gfx/surface/transport_dib.h" + +#include <unistd.h> +#include <sys/stat.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/shared_memory.h" +#include "skia/ext/platform_canvas.h" + +TransportDIB::TransportDIB() + : size_(0) { +} + +TransportDIB::TransportDIB(TransportDIB::Handle dib) + : shared_memory_(dib, false /* read write */), + size_(0) { +} + +TransportDIB::~TransportDIB() { +} + +// static +TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) { + TransportDIB* dib = new TransportDIB; + if (!dib->shared_memory_.CreateAndMapAnonymous(size)) { + delete dib; + return NULL; + } + + dib->size_ = size; + return dib; +} + +// static +TransportDIB* TransportDIB::Map(Handle handle) { + scoped_ptr<TransportDIB> dib(CreateWithHandle(handle)); + if (!dib->Map()) + return NULL; + return dib.release(); +} + +// static +TransportDIB* TransportDIB::CreateWithHandle(Handle handle) { + return new TransportDIB(handle); +} + +// static +bool TransportDIB::is_valid_handle(Handle dib) { + return dib.fd >= 0; +} + +// static +bool TransportDIB::is_valid_id(Id id) { + return id != 0; +} + +skia::PlatformCanvas* TransportDIB::GetPlatformCanvas(int w, int h) { + if (!memory() && !Map()) + return NULL; + scoped_ptr<skia::PlatformCanvas> canvas(new skia::PlatformCanvas); + if (!canvas->initialize(w, h, true, reinterpret_cast<uint8_t*>(memory()))) + return NULL; + return canvas.release(); +} + +bool TransportDIB::Map() { + if (!is_valid_handle(handle())) + return false; + if (memory()) + return true; + + struct stat st; + if ((fstat(shared_memory_.handle().fd, &st) != 0) || + (!shared_memory_.Map(st.st_size))) { + return false; + } + + size_ = st.st_size; + return true; +} + +void* TransportDIB::memory() const { + return shared_memory_.memory(); +} + +TransportDIB::Id TransportDIB::id() const { + return shared_memory_.id(); +} + +TransportDIB::Handle TransportDIB::handle() const { + return shared_memory_.handle(); +} diff --git a/ui/gfx/surface/transport_dib_win.cc b/ui/gfx/surface/transport_dib_win.cc new file mode 100644 index 0000000..977153f --- /dev/null +++ b/ui/gfx/surface/transport_dib_win.cc @@ -0,0 +1,110 @@ +// 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. + +#include "ui/gfx/surface/transport_dib.h" + +#include <windows.h> + +#include <limits> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/sys_info.h" +#include "skia/ext/platform_canvas.h" + +TransportDIB::TransportDIB() { +} + +TransportDIB::~TransportDIB() { +} + +TransportDIB::TransportDIB(HANDLE handle) + : shared_memory_(handle, false /* read write */) { +} + +// static +TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) { + size_t allocation_granularity = base::SysInfo::VMAllocationGranularity(); + size = size / allocation_granularity + 1; + size = size * allocation_granularity; + + TransportDIB* dib = new TransportDIB; + + if (!dib->shared_memory_.CreateAnonymous(size)) { + delete dib; + return NULL; + } + + dib->size_ = size; + dib->sequence_num_ = sequence_num; + + return dib; +} + +// static +TransportDIB* TransportDIB::Map(Handle handle) { + scoped_ptr<TransportDIB> dib(CreateWithHandle(handle)); + if (!dib->Map()) + return NULL; + return dib.release(); +} + +// static +TransportDIB* TransportDIB::CreateWithHandle(Handle handle) { + return new TransportDIB(handle); +} + +// static +bool TransportDIB::is_valid_handle(Handle dib) { + return dib != NULL; +} + +// static +bool TransportDIB::is_valid_id(TransportDIB::Id id) { + return is_valid_handle(id.handle); +} + +skia::PlatformCanvas* TransportDIB::GetPlatformCanvas(int w, int h) { + // This DIB already mapped the file into this process, but PlatformCanvas + // will map it again. + DCHECK(!memory()) << "Mapped file twice in the same process."; + + scoped_ptr<skia::PlatformCanvas> canvas(new skia::PlatformCanvas); + if (!canvas->initialize(w, h, true, handle())) + return NULL; + return canvas.release(); +} + +bool TransportDIB::Map() { + if (!is_valid_handle(handle())) + return false; + if (memory()) + return true; + + if (!shared_memory_.Map(0 /* map whole shared memory segment */)) { + LOG(ERROR) << "Failed to map transport DIB" + << " handle:" << shared_memory_.handle() + << " error:" << ::GetLastError(); + return false; + } + + // There doesn't seem to be any way to find the size of the shared memory + // region! GetFileSize indicates that the handle is invalid. Thus, we + // conservatively set the size to the maximum and hope that the renderer + // isn't about to ask us to read off the end of the array. + size_ = std::numeric_limits<size_t>::max(); + return true; +} + +void* TransportDIB::memory() const { + return shared_memory_.memory(); +} + +TransportDIB::Handle TransportDIB::handle() const { + return shared_memory_.handle(); +} + +TransportDIB::Id TransportDIB::id() const { + return Id(handle(), sequence_num_); +} |