Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / ui / surface / accelerated_surface_mac.cc
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.
4
5 #include "ui/surface/accelerated_surface_mac.h"
6
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"
15
16 // Note that this must be included after gl_bindings.h to avoid conflicts.
17 #include <OpenGL/CGLIOSurface.h>
18
19 AcceleratedSurface::AcceleratedSurface()
20     : io_surface_id_(0),
21       allocate_fbo_(false),
22       texture_(0),
23       fbo_(0) {
24 }
25
26 AcceleratedSurface::~AcceleratedSurface() {}
27
28 bool AcceleratedSurface::Initialize(
29     gfx::GLContext* share_context,
30     bool allocate_fbo,
31     gfx::GpuPreference gpu_preference) {
32   allocate_fbo_ = allocate_fbo;
33
34   // GL should be initialized by content::SupportsCoreAnimationPlugins().
35   DCHECK_NE(gfx::GetGLImplementation(), gfx::kGLImplementationNone);
36
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;
42
43   gl_surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(1, 1));
44   if (!gl_surface_.get()) {
45     Destroy();
46     return false;
47   }
48
49   gfx::GLShareGroup* share_group =
50       share_context ? share_context->share_group() : NULL;
51
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;
59   }
60
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;
65 }
66
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;
74 }
75
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();
106     }
107   }
108 }
109
110 static void AddBooleanValue(CFMutableDictionaryRef dictionary,
111                             const CFStringRef key,
112                             bool value) {
113   CFDictionaryAddValue(dictionary, key,
114                        (value ? kCFBooleanTrue : kCFBooleanFalse));
115 }
116
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());
123 }
124
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;
136 }
137
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_);
146   }
147
148   // Make sure that subsequent set-up code affects the render texture.
149   glBindTexture(target, texture_);
150 }
151
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_,
159                             0);
160   fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
161   return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT;
162 }
163
164 gfx::Size AcceleratedSurface::ClampToValidDimensions(const gfx::Size& size) {
165   return gfx::Size(std::max(size.width(), 1), std::max(size.height(), 1));
166 }
167
168 bool AcceleratedSurface::MakeCurrent() {
169   if (!gl_context_.get())
170     return false;
171   return gl_context_->MakeCurrent(gl_surface_.get());
172 }
173
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);
182 }
183
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;
189   }
190
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;
196
197   ui::ScopedMakeCurrent make_current(gl_context_.get(), gl_surface_.get());
198   if (!make_current.Succeeded())
199     return 0;
200
201   gfx::Size clamped_size = ClampToValidDimensions(size);
202
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);
211   }
212
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,
217                                              0,
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));
228
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";
243   }
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";
248     }
249   }
250   surface_size_ = size;
251   real_surface_size_ = clamped_size;
252
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_;
262 }
263
264 uint32 AcceleratedSurface::GetSurfaceId() {
265   return io_surface_id_;
266 }