| 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/accelerated_widget_mac/io_surface_texture.h" |  | 
| 6 |  | 
| 7 #include <OpenGL/CGLIOSurface.h> |  | 
| 8 #include <OpenGL/CGLRenderers.h> |  | 
| 9 #include <OpenGL/gl.h> |  | 
| 10 #include <OpenGL/OpenGL.h> |  | 
| 11 |  | 
| 12 #include "base/bind.h" |  | 
| 13 #include "base/bind_helpers.h" |  | 
| 14 #include "base/callback_helpers.h" |  | 
| 15 #include "base/command_line.h" |  | 
| 16 #include "base/logging.h" |  | 
| 17 #include "base/mac/bind_objc_block.h" |  | 
| 18 #include "base/message_loop/message_loop.h" |  | 
| 19 #include "base/threading/platform_thread.h" |  | 
| 20 #include "base/trace_event/trace_event.h" |  | 
| 21 #include "third_party/skia/include/core/SkBitmap.h" |  | 
| 22 #include "ui/accelerated_widget_mac/io_surface_context.h" |  | 
| 23 #include "ui/base/ui_base_switches.h" |  | 
| 24 #include "ui/gfx/geometry/rect.h" |  | 
| 25 #include "ui/gfx/geometry/size_conversions.h" |  | 
| 26 #include "ui/gl/gl_context.h" |  | 
| 27 |  | 
| 28 namespace ui { |  | 
| 29 |  | 
| 30 // static |  | 
| 31 scoped_refptr<IOSurfaceTexture> IOSurfaceTexture::Create( |  | 
| 32     bool needs_gl_finish_workaround, |  | 
| 33     bool use_ns_apis) { |  | 
| 34   scoped_refptr<IOSurfaceContext> offscreen_context; |  | 
| 35   if (!use_ns_apis) { |  | 
| 36     offscreen_context = IOSurfaceContext::Get( |  | 
| 37         IOSurfaceContext::kOffscreenContext); |  | 
| 38     if (!offscreen_context.get()) { |  | 
| 39       LOG(ERROR) << "Failed to create context for offscreen operations"; |  | 
| 40       return NULL; |  | 
| 41     } |  | 
| 42   } |  | 
| 43   return new IOSurfaceTexture( |  | 
| 44       offscreen_context, use_ns_apis, needs_gl_finish_workaround); |  | 
| 45 } |  | 
| 46 |  | 
| 47 IOSurfaceTexture::IOSurfaceTexture( |  | 
| 48     const scoped_refptr<IOSurfaceContext>& offscreen_context, |  | 
| 49     bool use_ns_apis, |  | 
| 50     bool needs_gl_finish_workaround) |  | 
| 51     : offscreen_context_(offscreen_context), |  | 
| 52       texture_(0), |  | 
| 53       gl_error_(GL_NO_ERROR), |  | 
| 54       eviction_queue_iterator_(eviction_queue_.Get().end()), |  | 
| 55       eviction_has_been_drawn_since_updated_(false), |  | 
| 56       needs_gl_finish_workaround_(needs_gl_finish_workaround), |  | 
| 57       using_ns_apis_(use_ns_apis) { |  | 
| 58 } |  | 
| 59 |  | 
| 60 IOSurfaceTexture::~IOSurfaceTexture() { |  | 
| 61   ReleaseIOSurfaceAndTexture(); |  | 
| 62   offscreen_context_ = NULL; |  | 
| 63   DCHECK(eviction_queue_iterator_ == eviction_queue_.Get().end()); |  | 
| 64 } |  | 
| 65 |  | 
| 66 bool IOSurfaceTexture::DrawIOSurface() { |  | 
| 67   return DrawIOSurfaceWithDamageRect(gfx::Rect(pixel_size_)); |  | 
| 68 } |  | 
| 69 |  | 
| 70 bool IOSurfaceTexture::DrawIOSurfaceWithDamageRect(gfx::Rect damage_rect) { |  | 
| 71   TRACE_EVENT0("browser", "IOSurfaceTexture::DrawIOSurfaceWithDamageRect"); |  | 
| 72   DCHECK(CGLGetCurrentContext()); |  | 
| 73 |  | 
| 74   // If we have release the IOSurface, clear the screen to light grey and |  | 
| 75   // early-out. |  | 
| 76   if (!io_surface_) { |  | 
| 77     glClearColor(0.9, 0.9, 0.9, 1); |  | 
| 78     glClear(GL_COLOR_BUFFER_BIT); |  | 
| 79     return false; |  | 
| 80   } |  | 
| 81 |  | 
| 82   // The viewport is the size of the CALayer, which should always match the |  | 
| 83   // IOSurface pixel size. |  | 
| 84   GLint viewport[4]; |  | 
| 85   glGetIntegerv(GL_VIEWPORT, viewport); |  | 
| 86   gfx::Rect viewport_rect(viewport[0], viewport[1], viewport[2], viewport[3]); |  | 
| 87 |  | 
| 88   // Set the projection matrix to match 1 unit to 1 pixel. |  | 
| 89   glMatrixMode(GL_PROJECTION); |  | 
| 90   glLoadIdentity(); |  | 
| 91   glOrtho(0, viewport_rect.width(), |  | 
| 92           pixel_size_.height() - viewport_rect.height(), pixel_size_.height(), |  | 
| 93           -1, 1); |  | 
| 94   glMatrixMode(GL_MODELVIEW); |  | 
| 95   glLoadIdentity(); |  | 
| 96 |  | 
| 97   // Draw a quad the size of the IOSurface. This should cover the full viewport. |  | 
| 98   glColor4f(1, 1, 1, 1); |  | 
| 99   glEnable(GL_TEXTURE_RECTANGLE_ARB); |  | 
| 100   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_); |  | 
| 101   glBegin(GL_QUADS); |  | 
| 102   glTexCoord2f(damage_rect.x(), damage_rect.y()); |  | 
| 103   glVertex2f(damage_rect.x(), damage_rect.y()); |  | 
| 104   glTexCoord2f(damage_rect.right(), damage_rect.y()); |  | 
| 105   glVertex2f(damage_rect.right(), damage_rect.y()); |  | 
| 106   glTexCoord2f(damage_rect.right(), damage_rect.bottom()); |  | 
| 107   glVertex2f(damage_rect.right(), damage_rect.bottom()); |  | 
| 108   glTexCoord2f(damage_rect.x(), damage_rect.bottom()); |  | 
| 109   glVertex2f(damage_rect.x(), damage_rect.bottom()); |  | 
| 110   glEnd(); |  | 
| 111   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |  | 
| 112   glDisable(GL_TEXTURE_RECTANGLE_ARB); |  | 
| 113 |  | 
| 114   // Workaround for issue 158469. Issue a dummy draw call with texture_ not |  | 
| 115   // bound to a texture, in order to shake all references to the IOSurface out |  | 
| 116   // of the driver. |  | 
| 117   glBegin(GL_TRIANGLES); |  | 
| 118   glEnd(); |  | 
| 119 |  | 
| 120   if (needs_gl_finish_workaround_) { |  | 
| 121     TRACE_EVENT0("gpu", "glFinish"); |  | 
| 122     glFinish(); |  | 
| 123   } |  | 
| 124 |  | 
| 125   // Check if any of the drawing calls result in an error. |  | 
| 126   GetAndSaveGLError(); |  | 
| 127   bool result = true; |  | 
| 128   if (gl_error_ != GL_NO_ERROR) { |  | 
| 129     LOG(ERROR) << "GL error in DrawIOSurface: " << gl_error_; |  | 
| 130     result = false; |  | 
| 131     // If there was an error, clear the screen to a light grey to avoid |  | 
| 132     // rendering artifacts. |  | 
| 133     glClearColor(0.8, 0.8, 0.8, 1.0); |  | 
| 134     glClear(GL_COLOR_BUFFER_BIT); |  | 
| 135   } |  | 
| 136 |  | 
| 137   eviction_has_been_drawn_since_updated_ = true; |  | 
| 138   return result; |  | 
| 139 } |  | 
| 140 |  | 
| 141 bool IOSurfaceTexture::IsUpToDate( |  | 
| 142     IOSurfaceID io_surface_id, const gfx::Size& pixel_size) const { |  | 
| 143   return io_surface_ && |  | 
| 144          io_surface_id == IOSurfaceGetID(io_surface_) && |  | 
| 145          pixel_size == pixel_size_; |  | 
| 146 } |  | 
| 147 |  | 
| 148 bool IOSurfaceTexture::SetIOSurface( |  | 
| 149     IOSurfaceID io_surface_id, const gfx::Size& pixel_size) { |  | 
| 150   TRACE_EVENT0("browser", "IOSurfaceTexture::MapIOSurfaceToTexture"); |  | 
| 151 |  | 
| 152   // Destroy the old IOSurface and texture if it is no longer needed. |  | 
| 153   bool needs_new_iosurface = |  | 
| 154       !io_surface_ || io_surface_id != IOSurfaceGetID(io_surface_); |  | 
| 155   if (needs_new_iosurface) |  | 
| 156     ReleaseIOSurfaceAndTexture(); |  | 
| 157 |  | 
| 158   // Note that because IOSurface sizes are rounded, the same IOSurface may have |  | 
| 159   // two different sizes associated with it, so update the sizes before the |  | 
| 160   // early-out. |  | 
| 161   pixel_size_ = pixel_size; |  | 
| 162 |  | 
| 163   // Early-out if the IOSurface has not changed. |  | 
| 164   if (!needs_new_iosurface) |  | 
| 165     return true; |  | 
| 166 |  | 
| 167   // If we early-out at any point from now on, it's because of an error, and we |  | 
| 168   // should destroy the texture and release the IOSurface. |  | 
| 169   base::ScopedClosureRunner error_runner(base::BindBlock(^{ |  | 
| 170       ReleaseIOSurfaceAndTexture(); |  | 
| 171   })); |  | 
| 172 |  | 
| 173   // Open the IOSurface handle. |  | 
| 174   io_surface_.reset(IOSurfaceLookup(io_surface_id)); |  | 
| 175   if (!io_surface_) |  | 
| 176     return false; |  | 
| 177 |  | 
| 178   // Actual IOSurface size is rounded up to reduce reallocations during window |  | 
| 179   // resize. Get the actual size to properly map the texture. |  | 
| 180   gfx::Size rounded_size(IOSurfaceGetWidth(io_surface_), |  | 
| 181                          IOSurfaceGetHeight(io_surface_)); |  | 
| 182 |  | 
| 183   // Create the GL texture and set it to be backed by the IOSurface. |  | 
| 184   CGLError cgl_error = kCGLNoError; |  | 
| 185   { |  | 
| 186     scoped_ptr<gfx::ScopedCGLSetCurrentContext> scoped_set_current_context; |  | 
| 187     if (offscreen_context_) { |  | 
| 188       scoped_set_current_context.reset(new gfx::ScopedCGLSetCurrentContext( |  | 
| 189           offscreen_context_->cgl_context())); |  | 
| 190     } else { |  | 
| 191       DCHECK(CGLGetCurrentContext()); |  | 
| 192     } |  | 
| 193 |  | 
| 194     glGenTextures(1, &texture_); |  | 
| 195     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_); |  | 
| 196     glTexParameterf( |  | 
| 197         GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |  | 
| 198     glTexParameterf( |  | 
| 199         GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |  | 
| 200     cgl_error = CGLTexImageIOSurface2D( |  | 
| 201         CGLGetCurrentContext(), |  | 
| 202         GL_TEXTURE_RECTANGLE_ARB, |  | 
| 203         GL_RGBA, |  | 
| 204         rounded_size.width(), |  | 
| 205         rounded_size.height(), |  | 
| 206         GL_BGRA, |  | 
| 207         GL_UNSIGNED_INT_8_8_8_8_REV, |  | 
| 208         io_surface_.get(), |  | 
| 209         0 /* plane */); |  | 
| 210     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |  | 
| 211     GetAndSaveGLError(); |  | 
| 212   } |  | 
| 213 |  | 
| 214   // Return failure if an error was encountered by CGL or GL. |  | 
| 215   if (cgl_error != kCGLNoError) { |  | 
| 216     LOG(ERROR) << "CGLTexImageIOSurface2D failed with CGL error: " << cgl_error; |  | 
| 217     return false; |  | 
| 218   } |  | 
| 219   if (gl_error_ != GL_NO_ERROR) { |  | 
| 220     LOG(ERROR) << "Hit GL error in SetIOSurface: " << gl_error_; |  | 
| 221     return false; |  | 
| 222   } |  | 
| 223 |  | 
| 224   ignore_result(error_runner.Release()); |  | 
| 225   return true; |  | 
| 226 } |  | 
| 227 |  | 
| 228 void IOSurfaceTexture::ReleaseIOSurfaceAndTexture() { |  | 
| 229   scoped_ptr<gfx::ScopedCGLSetCurrentContext> scoped_set_current_context; |  | 
| 230   if (offscreen_context_) { |  | 
| 231     scoped_set_current_context.reset(new gfx::ScopedCGLSetCurrentContext( |  | 
| 232         offscreen_context_->cgl_context())); |  | 
| 233   } else { |  | 
| 234     DCHECK(CGLGetCurrentContext()); |  | 
| 235   } |  | 
| 236 |  | 
| 237   if (texture_) { |  | 
| 238     glDeleteTextures(1, &texture_); |  | 
| 239     texture_ = 0; |  | 
| 240   } |  | 
| 241   pixel_size_ = gfx::Size(); |  | 
| 242   io_surface_.reset(); |  | 
| 243 |  | 
| 244   EvictionMarkEvicted(); |  | 
| 245 } |  | 
| 246 |  | 
| 247 bool IOSurfaceTexture::HasBeenPoisoned() const { |  | 
| 248   if (offscreen_context_) |  | 
| 249     return offscreen_context_->HasBeenPoisoned(); |  | 
| 250   return false; |  | 
| 251 } |  | 
| 252 |  | 
| 253 GLenum IOSurfaceTexture::GetAndSaveGLError() { |  | 
| 254   GLenum gl_error = glGetError(); |  | 
| 255   if (gl_error_ == GL_NO_ERROR) |  | 
| 256     gl_error_ = gl_error; |  | 
| 257   return gl_error; |  | 
| 258 } |  | 
| 259 |  | 
| 260 void IOSurfaceTexture::EvictionMarkUpdated() { |  | 
| 261   EvictionMarkEvicted(); |  | 
| 262   eviction_queue_.Get().push_back(this); |  | 
| 263   eviction_queue_iterator_ = --eviction_queue_.Get().end(); |  | 
| 264   eviction_has_been_drawn_since_updated_ = false; |  | 
| 265   EvictionScheduleDoEvict(); |  | 
| 266 } |  | 
| 267 |  | 
| 268 void IOSurfaceTexture::EvictionMarkEvicted() { |  | 
| 269   if (eviction_queue_iterator_ == eviction_queue_.Get().end()) |  | 
| 270     return; |  | 
| 271   eviction_queue_.Get().erase(eviction_queue_iterator_); |  | 
| 272   eviction_queue_iterator_ = eviction_queue_.Get().end(); |  | 
| 273   eviction_has_been_drawn_since_updated_ = false; |  | 
| 274 } |  | 
| 275 |  | 
| 276 // static |  | 
| 277 void IOSurfaceTexture::EvictionScheduleDoEvict() { |  | 
| 278   if (eviction_scheduled_) |  | 
| 279     return; |  | 
| 280   if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces) |  | 
| 281     return; |  | 
| 282 |  | 
| 283   eviction_scheduled_ = true; |  | 
| 284   base::MessageLoop::current()->PostTask( |  | 
| 285       FROM_HERE, |  | 
| 286       base::Bind(&IOSurfaceTexture::EvictionDoEvict)); |  | 
| 287 } |  | 
| 288 |  | 
| 289 // static |  | 
| 290 void IOSurfaceTexture::EvictionDoEvict() { |  | 
| 291   eviction_scheduled_ = false; |  | 
| 292   // Walk the list of allocated surfaces from least recently used to most |  | 
| 293   // recently used. |  | 
| 294   for (EvictionQueue::iterator it = eviction_queue_.Get().begin(); |  | 
| 295        it != eviction_queue_.Get().end();) { |  | 
| 296     IOSurfaceTexture* surface = *it; |  | 
| 297     ++it; |  | 
| 298 |  | 
| 299     // If the number of IOSurfaces allocated is less than the threshold, |  | 
| 300     // stop walking the list of surfaces. |  | 
| 301     if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces) |  | 
| 302       break; |  | 
| 303 |  | 
| 304     // Don't evict anything that has not yet been drawn. |  | 
| 305     if (!surface->eviction_has_been_drawn_since_updated_) |  | 
| 306       continue; |  | 
| 307 |  | 
| 308     // Don't evict anything that doesn't have an offscreen context (as we have |  | 
| 309     // context in which to delete the texture). |  | 
| 310     if (!surface->offscreen_context_) |  | 
| 311       continue; |  | 
| 312 |  | 
| 313     // Evict the surface. |  | 
| 314     surface->ReleaseIOSurfaceAndTexture(); |  | 
| 315   } |  | 
| 316 } |  | 
| 317 |  | 
| 318 // static |  | 
| 319 base::LazyInstance<IOSurfaceTexture::EvictionQueue> |  | 
| 320     IOSurfaceTexture::eviction_queue_; |  | 
| 321 |  | 
| 322 // static |  | 
| 323 bool IOSurfaceTexture::eviction_scheduled_ = false; |  | 
| 324 |  | 
| 325 }  // namespace ui |  | 
| OLD | NEW | 
|---|