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"
15 #include "ui/surface/io_surface_support_mac.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 // Ensure GL is initialized before trying to create an offscreen GL context.
33 if (!gfx::GLSurface::InitializeOneOff())
36 // Drawing to IOSurfaces via OpenGL only works with Apple's GL and
37 // not with the OSMesa software renderer.
38 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL
&&
39 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL
)
42 gl_surface_
= gfx::GLSurface::CreateOffscreenGLSurface(
43 false, gfx::Size(1, 1));
44 if (!gl_surface_
.get()) {
49 gfx::GLShareGroup
* share_group
=
50 share_context
? share_context
->share_group() : NULL
;
52 gl_context_
= gfx::GLContext::CreateGLContext(
56 if (!gl_context_
.get()) {
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.
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
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
) {
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_
);
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
, ¤t_texture
);
96 glBindTexture(target
, texture_
);
97 glCopyTexSubImage2D(target
, 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.
110 static void AddBooleanValue(CFMutableDictionaryRef dictionary
,
111 const CFStringRef key
,
113 CFDictionaryAddValue(dictionary
, key
,
114 (value
? kCFBooleanTrue
: kCFBooleanFalse
));
117 static void AddIntegerValue(CFMutableDictionaryRef dictionary
,
118 const CFStringRef key
,
120 base::mac::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
) {
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
);
138 void AcceleratedSurface::AllocateRenderBuffers(GLenum target
,
139 const gfx::Size
& size
) {
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_
);
155 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
,
156 GL_COLOR_ATTACHMENT0_EXT
,
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())
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
);
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.
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
194 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL
)
197 IOSurfaceSupport
* io_surface_support
= IOSurfaceSupport::Initialize();
198 if (!io_surface_support
)
201 ui::ScopedMakeCurrent
make_current(gl_context_
.get(), gl_surface_
.get());
202 if (!make_current
.Succeeded())
205 gfx::Size clamped_size
= ClampToValidDimensions(size
);
207 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
208 // Mac OS X and is required for IOSurface interoperability.
209 GLenum target
= GL_TEXTURE_RECTANGLE_ARB
;
211 AllocateRenderBuffers(target
, clamped_size
);
212 } else if (!texture_
) {
213 // Generate the texture object.
214 texture_
= CreateTexture(target
);
217 // Allocate a new IOSurface, which is the GPU resource that can be
218 // shared across processes.
219 base::mac::ScopedCFTypeRef
<CFMutableDictionaryRef
> properties
;
220 properties
.reset(CFDictionaryCreateMutable(kCFAllocatorDefault
,
222 &kCFTypeDictionaryKeyCallBacks
,
223 &kCFTypeDictionaryValueCallBacks
));
224 AddIntegerValue(properties
,
225 io_surface_support
->GetKIOSurfaceWidth(),
226 clamped_size
.width());
227 AddIntegerValue(properties
,
228 io_surface_support
->GetKIOSurfaceHeight(),
229 clamped_size
.height());
230 AddIntegerValue(properties
,
231 io_surface_support
->GetKIOSurfaceBytesPerElement(), 4);
232 AddBooleanValue(properties
,
233 io_surface_support
->GetKIOSurfaceIsGlobal(), true);
234 // I believe we should be able to unreference the IOSurfaces without
235 // synchronizing with the browser process because they are
236 // ultimately reference counted by the operating system.
237 io_surface_
.reset(io_surface_support
->IOSurfaceCreate(properties
));
239 // Don't think we need to identify a plane.
241 CGLError error
= io_surface_support
->CGLTexImageIOSurface2D(
242 static_cast<CGLContextObj
>(gl_context_
->GetHandle()),
245 clamped_size
.width(),
246 clamped_size
.height(),
248 GL_UNSIGNED_INT_8_8_8_8_REV
,
251 if (error
!= kCGLNoError
) {
252 DLOG(ERROR
) << "CGL error " << error
<< " during CGLTexImageIOSurface2D";
255 // Set up the frame buffer object.
256 if (!SetupFrameBufferObject(target
)) {
257 DLOG(ERROR
) << "Failed to set up frame buffer object";
260 surface_size_
= size
;
261 real_surface_size_
= clamped_size
;
263 // Now send back an identifier for the IOSurface. We originally
264 // intended to send back a mach port from IOSurfaceCreateMachPort
265 // but it looks like Chrome IPC would need to be modified to
266 // properly send mach ports between processes. For the time being we
267 // make our IOSurfaces global and send back their identifiers. On
268 // the browser process side the identifier is reconstituted into an
269 // IOSurface for on-screen rendering.
270 io_surface_id_
= io_surface_support
->IOSurfaceGetID(io_surface_
);
271 return io_surface_id_
;
274 uint32
AcceleratedSurface::GetSurfaceId() {
275 return io_surface_id_
;