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/io_surface_support_mac.h"
15 #include "ui/gl/scoped_make_current.h"
17 AcceleratedSurface::AcceleratedSurface()
24 AcceleratedSurface::~AcceleratedSurface() {}
26 bool AcceleratedSurface::Initialize(
27 gfx::GLContext* share_context,
29 gfx::GpuPreference gpu_preference) {
30 allocate_fbo_ = allocate_fbo;
32 // GL should be initialized by content::SupportsCoreAnimationPlugins().
33 DCHECK_NE(gfx::GetGLImplementation(), gfx::kGLImplementationNone);
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)
41 gl_surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(1, 1));
42 if (!gl_surface_.get()) {
47 gfx::GLShareGroup* share_group =
48 share_context ? share_context->share_group() : NULL;
50 gl_context_ = gfx::GLContext::CreateGLContext(
54 if (!gl_context_.get()) {
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.
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
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) {
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_);
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, ¤t_texture);
94 glBindTexture(target, texture_);
95 glCopyTexSubImage2D(target, 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.
108 static void AddBooleanValue(CFMutableDictionaryRef dictionary,
109 const CFStringRef key,
111 CFDictionaryAddValue(dictionary, key,
112 (value ? kCFBooleanTrue : kCFBooleanFalse));
115 static void AddIntegerValue(CFMutableDictionaryRef dictionary,
116 const CFStringRef key,
118 base::ScopedCFTypeRef<CFNumberRef> number(
119 CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
120 CFDictionaryAddValue(dictionary, key, number.get());
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) {
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);
136 void AcceleratedSurface::AllocateRenderBuffers(GLenum target,
137 const gfx::Size& size) {
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_);
146 // Make sure that subsequent set-up code affects the render texture.
147 glBindTexture(target, texture_);
150 bool AcceleratedSurface::SetupFrameBufferObject(GLenum target) {
151 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
153 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
154 GL_COLOR_ATTACHMENT0_EXT,
158 fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
159 return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT;
162 gfx::Size AcceleratedSurface::ClampToValidDimensions(const gfx::Size& size) {
163 return gfx::Size(std::max(size.width(), 1), std::max(size.height(), 1));
166 bool AcceleratedSurface::MakeCurrent() {
167 if (!gl_context_.get())
169 return gl_context_->MakeCurrent(gl_surface_.get());
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);
178 glOrtho(0, rect.width(), 0, rect.height(), -1, 1);
179 glClear(GL_COLOR_BUFFER_BIT);
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.
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
192 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL)
195 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
196 if (!io_surface_support)
199 ui::ScopedMakeCurrent make_current(gl_context_.get(), gl_surface_.get());
200 if (!make_current.Succeeded())
203 gfx::Size clamped_size = ClampToValidDimensions(size);
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;
209 AllocateRenderBuffers(target, clamped_size);
210 } else if (!texture_) {
211 // Generate the texture object.
212 texture_ = CreateTexture(target);
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,
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));
237 // Don't think we need to identify a plane.
239 CGLError error = io_surface_support->CGLTexImageIOSurface2D(
240 static_cast<CGLContextObj>(gl_context_->GetHandle()),
243 clamped_size.width(),
244 clamped_size.height(),
246 GL_UNSIGNED_INT_8_8_8_8_REV,
249 if (error != kCGLNoError) {
250 DLOG(ERROR) << "CGL error " << error << " during CGLTexImageIOSurface2D";
253 // Set up the frame buffer object.
254 if (!SetupFrameBufferObject(target)) {
255 DLOG(ERROR) << "Failed to set up frame buffer object";
258 surface_size_ = size;
259 real_surface_size_ = clamped_size;
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_;
272 uint32 AcceleratedSurface::GetSurfaceId() {
273 return io_surface_id_;