crazy linker: Fix failure to cleanly unload libraries.
[chromium-blink-merge.git] / ui / surface / accelerated_surface_mac.cc
blob2fbd47adaef5af44af4a62c5e28d97c5ee8549e3
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/surface/accelerated_surface_mac.h"
7 #include "base/logging.h"
8 #include "base/mac/scoped_cftyperef.h"
9 #include "ui/gfx/rect.h"
10 #include "ui/gl/gl_bindings.h"
11 #include "ui/gl/gl_context.h"
12 #include "ui/gl/gl_implementation.h"
13 #include "ui/gl/gl_surface.h"
14 #include "ui/gl/scoped_make_current.h"
16 // Note that this must be included after gl_bindings.h to avoid conflicts.
17 #include <OpenGL/CGLIOSurface.h>
19 AcceleratedSurface::AcceleratedSurface()
20 : io_surface_id_(0),
21 allocate_fbo_(false),
22 texture_(0),
23 fbo_(0) {
26 AcceleratedSurface::~AcceleratedSurface() {}
28 bool AcceleratedSurface::Initialize(
29 gfx::GLContext* share_context,
30 bool allocate_fbo,
31 gfx::GpuPreference gpu_preference) {
32 allocate_fbo_ = allocate_fbo;
34 // GL should be initialized by content::SupportsCoreAnimationPlugins().
35 DCHECK_NE(gfx::GetGLImplementation(), gfx::kGLImplementationNone);
37 // Drawing to IOSurfaces via OpenGL only works with Apple's GL and
38 // not with the OSMesa software renderer.
39 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
40 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
41 return false;
43 gl_surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(1, 1));
44 if (!gl_surface_.get()) {
45 Destroy();
46 return false;
49 gfx::GLShareGroup* share_group =
50 share_context ? share_context->share_group() : NULL;
52 gl_context_ = gfx::GLContext::CreateGLContext(
53 share_group,
54 gl_surface_.get(),
55 gpu_preference);
56 if (!gl_context_.get()) {
57 Destroy();
58 return false;
61 // Now we're ready to handle SetSurfaceSize calls, which will
62 // allocate and/or reallocate the IOSurface and associated offscreen
63 // OpenGL structures for rendering.
64 return true;
67 void AcceleratedSurface::Destroy() {
68 // The FBO and texture objects will be destroyed when the OpenGL context,
69 // and any other contexts sharing resources with it, is. We don't want to
70 // make the context current one last time here just in order to delete
71 // these objects.
72 gl_context_ = NULL;
73 gl_surface_ = NULL;
76 // Call after making changes to the surface which require a visual update.
77 // Makes the rendering show up in other processes.
78 void AcceleratedSurface::SwapBuffers() {
79 if (io_surface_.get() != NULL) {
80 if (allocate_fbo_) {
81 // Bind and unbind the framebuffer to make changes to the
82 // IOSurface show up in the other process.
83 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
84 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
85 glFlush();
86 } else {
87 // Copy the current framebuffer's contents into our "live" texture.
88 // Note that the current GL context might not be ours at this point!
89 // This is deliberate, so that surrounding code using GL can produce
90 // rendering results consumed by the AcceleratedSurface.
91 // Need to save and restore OpenGL state around this call.
92 GLint current_texture = 0;
93 GLenum target_binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
94 GLenum target = GL_TEXTURE_RECTANGLE_ARB;
95 glGetIntegerv(target_binding, &current_texture);
96 glBindTexture(target, texture_);
97 glCopyTexSubImage2D(target, 0,
98 0, 0,
99 0, 0,
100 real_surface_size_.width(),
101 real_surface_size_.height());
102 glBindTexture(target, current_texture);
103 // This flush is absolutely essential -- it guarantees that the
104 // rendering results are seen by the other process.
105 glFlush();
110 static void AddBooleanValue(CFMutableDictionaryRef dictionary,
111 const CFStringRef key,
112 bool value) {
113 CFDictionaryAddValue(dictionary, key,
114 (value ? kCFBooleanTrue : kCFBooleanFalse));
117 static void AddIntegerValue(CFMutableDictionaryRef dictionary,
118 const CFStringRef key,
119 int32 value) {
120 base::ScopedCFTypeRef<CFNumberRef> number(
121 CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
122 CFDictionaryAddValue(dictionary, key, number.get());
125 // Creates a new OpenGL texture object bound to the given texture target.
126 // Caller owns the returned texture.
127 static GLuint CreateTexture(GLenum target) {
128 GLuint texture = 0;
129 glGenTextures(1, &texture);
130 glBindTexture(target, texture);
131 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
132 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
133 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
134 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
135 return texture;
138 void AcceleratedSurface::AllocateRenderBuffers(GLenum target,
139 const gfx::Size& size) {
140 if (!texture_) {
141 // Generate the texture object.
142 texture_ = CreateTexture(target);
143 // Generate and bind the framebuffer object.
144 glGenFramebuffersEXT(1, &fbo_);
145 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
148 // Make sure that subsequent set-up code affects the render texture.
149 glBindTexture(target, texture_);
152 bool AcceleratedSurface::SetupFrameBufferObject(GLenum target) {
153 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
154 GLenum fbo_status;
155 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
156 GL_COLOR_ATTACHMENT0_EXT,
157 target,
158 texture_,
160 fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
161 return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT;
164 gfx::Size AcceleratedSurface::ClampToValidDimensions(const gfx::Size& size) {
165 return gfx::Size(std::max(size.width(), 1), std::max(size.height(), 1));
168 bool AcceleratedSurface::MakeCurrent() {
169 if (!gl_context_.get())
170 return false;
171 return gl_context_->MakeCurrent(gl_surface_.get());
174 void AcceleratedSurface::Clear(const gfx::Rect& rect) {
175 DCHECK(gl_context_->IsCurrent(gl_surface_.get()));
176 glClearColor(0, 0, 0, 0);
177 glViewport(0, 0, rect.width(), rect.height());
178 glMatrixMode(GL_PROJECTION);
179 glLoadIdentity();
180 glOrtho(0, rect.width(), 0, rect.height(), -1, 1);
181 glClear(GL_COLOR_BUFFER_BIT);
184 uint32 AcceleratedSurface::SetSurfaceSize(const gfx::Size& size) {
185 if (surface_size_ == size) {
186 // Return 0 to indicate to the caller that no new backing store
187 // allocation occurred.
188 return 0;
191 // Only support IO surfaces if the GL implementation is the native desktop GL.
192 // IO surfaces will not work with, for example, OSMesa software renderer
193 // GL contexts.
194 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL)
195 return 0;
197 ui::ScopedMakeCurrent make_current(gl_context_.get(), gl_surface_.get());
198 if (!make_current.Succeeded())
199 return 0;
201 gfx::Size clamped_size = ClampToValidDimensions(size);
203 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
204 // Mac OS X and is required for IOSurface interoperability.
205 GLenum target = GL_TEXTURE_RECTANGLE_ARB;
206 if (allocate_fbo_) {
207 AllocateRenderBuffers(target, clamped_size);
208 } else if (!texture_) {
209 // Generate the texture object.
210 texture_ = CreateTexture(target);
213 // Allocate a new IOSurface, which is the GPU resource that can be
214 // shared across processes.
215 base::ScopedCFTypeRef<CFMutableDictionaryRef> properties;
216 properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault,
218 &kCFTypeDictionaryKeyCallBacks,
219 &kCFTypeDictionaryValueCallBacks));
220 AddIntegerValue(properties, kIOSurfaceWidth, clamped_size.width());
221 AddIntegerValue(properties, kIOSurfaceHeight, clamped_size.height());
222 AddIntegerValue(properties, kIOSurfaceBytesPerElement, 4);
223 AddBooleanValue(properties, kIOSurfaceIsGlobal, true);
224 // I believe we should be able to unreference the IOSurfaces without
225 // synchronizing with the browser process because they are
226 // ultimately reference counted by the operating system.
227 io_surface_.reset(IOSurfaceCreate(properties));
229 // Don't think we need to identify a plane.
230 GLuint plane = 0;
231 CGLError error = CGLTexImageIOSurface2D(
232 static_cast<CGLContextObj>(gl_context_->GetHandle()),
233 target,
234 GL_RGBA,
235 clamped_size.width(),
236 clamped_size.height(),
237 GL_BGRA,
238 GL_UNSIGNED_INT_8_8_8_8_REV,
239 io_surface_.get(),
240 plane);
241 if (error != kCGLNoError) {
242 DLOG(ERROR) << "CGL error " << error << " during CGLTexImageIOSurface2D";
244 if (allocate_fbo_) {
245 // Set up the frame buffer object.
246 if (!SetupFrameBufferObject(target)) {
247 DLOG(ERROR) << "Failed to set up frame buffer object";
250 surface_size_ = size;
251 real_surface_size_ = clamped_size;
253 // Now send back an identifier for the IOSurface. We originally
254 // intended to send back a mach port from IOSurfaceCreateMachPort
255 // but it looks like Chrome IPC would need to be modified to
256 // properly send mach ports between processes. For the time being we
257 // make our IOSurfaces global and send back their identifiers. On
258 // the browser process side the identifier is reconstituted into an
259 // IOSurface for on-screen rendering.
260 io_surface_id_ = IOSurfaceGetID(io_surface_);
261 return io_surface_id_;
264 uint32 AcceleratedSurface::GetSurfaceId() {
265 return io_surface_id_;