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