| OLD | NEW |
| (Empty) |
| 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/geometry/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, ¤t_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_t 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_t 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_t AcceleratedSurface::GetSurfaceId() { | |
| 265 return io_surface_id_; | |
| 266 } | |
| OLD | NEW |