Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / compositing_iosurface_mac.mm
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 "content/browser/renderer_host/compositing_iosurface_mac.h"
6
7 #include <OpenGL/CGLIOSurface.h>
8 #include <OpenGL/CGLRenderers.h>
9 #include <OpenGL/OpenGL.h>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/debug/trace_event.h"
14 #include "base/logging.h"
15 #include "base/mac/mac_util.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/threading/platform_thread.h"
18 #include "content/browser/gpu/gpu_data_manager_impl.h"
19 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
20 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h"
21 #include "content/browser/renderer_host/compositing_iosurface_transformer_mac.h"
22 #include "content/browser/renderer_host/render_widget_host_impl.h"
23 #include "content/browser/renderer_host/render_widget_host_view_mac.h"
24 #include "content/common/content_constants_internal.h"
25 #include "gpu/config/gpu_driver_bug_workaround_type.h"
26 #include "media/base/video_util.h"
27 #include "third_party/skia/include/core/SkBitmap.h"
28 #include "ui/gfx/rect.h"
29 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
30 #include "ui/gfx/size_conversions.h"
31 #include "ui/gl/gl_context.h"
32
33 #ifdef NDEBUG
34 #define CHECK_GL_ERROR()
35 #define CHECK_AND_SAVE_GL_ERROR()
36 #else
37 #define CHECK_GL_ERROR() do {                                           \
38     GLenum gl_error = glGetError();                                     \
39     LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \
40   } while (0)
41 #define CHECK_AND_SAVE_GL_ERROR() do {                                  \
42     GLenum gl_error = GetAndSaveGLError();                              \
43     LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \
44   } while (0)
45 #endif
46
47 namespace content {
48 namespace {
49
50 // How many times to test if asynchronous copy has completed.
51 // This value is chosen such that we allow at most 1 second to finish a copy.
52 const int kFinishCopyRetryCycles = 100;
53
54 // Time in milliseconds to allow asynchronous copy to finish.
55 // This value is shorter than 16ms such that copy can complete within a vsync.
56 const int kFinishCopyPollingPeriodMs = 10;
57
58 bool HasAppleFenceExtension() {
59   static bool initialized_has_fence = false;
60   static bool has_fence = false;
61
62   if (!initialized_has_fence) {
63     has_fence =
64         strstr(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)),
65                "GL_APPLE_fence") != NULL;
66     initialized_has_fence = true;
67   }
68   return has_fence;
69 }
70
71 bool HasPixelBufferObjectExtension() {
72   static bool initialized_has_pbo = false;
73   static bool has_pbo = false;
74
75   if (!initialized_has_pbo) {
76     has_pbo =
77         strstr(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)),
78                "GL_ARB_pixel_buffer_object") != NULL;
79     initialized_has_pbo = true;
80   }
81   return has_pbo;
82 }
83
84 // Helper function to reverse the argument order.  Also takes ownership of
85 // |bitmap_output| for the life of the binding.
86 void ReverseArgumentOrder(
87     const base::Callback<void(bool, const SkBitmap&)>& callback,
88     scoped_ptr<SkBitmap> bitmap_output, bool success) {
89   callback.Run(success, *bitmap_output);
90 }
91
92 // Called during an async GPU readback with a pointer to the pixel buffer.  In
93 // the snapshot path, we just memcpy the data into our output bitmap since the
94 // width, height, and stride should all be equal.
95 bool MapBufferToSkBitmap(const SkBitmap* output, const void* buf, int ignored) {
96   TRACE_EVENT0("browser", "MapBufferToSkBitmap");
97
98   if (buf) {
99     SkAutoLockPixels output_lock(*output);
100     memcpy(output->getPixels(), buf, output->getSize());
101   }
102   return buf != NULL;
103 }
104
105 // Copies tightly-packed scanlines from |buf| to |region_in_frame| in the given
106 // |target| VideoFrame's |plane|.  Assumption: |buf|'s width is
107 // |region_in_frame.width()| and its stride is always in 4-byte alignment.
108 //
109 // TODO(miu): Refactor by moving this function into media/video_util.
110 // http://crbug.com/219779
111 bool MapBufferToVideoFrame(
112     const scoped_refptr<media::VideoFrame>& target,
113     const gfx::Rect& region_in_frame,
114     const void* buf,
115     int plane) {
116   COMPILE_ASSERT(media::VideoFrame::kYPlane == 0, VideoFrame_kYPlane_mismatch);
117   COMPILE_ASSERT(media::VideoFrame::kUPlane == 1, VideoFrame_kUPlane_mismatch);
118   COMPILE_ASSERT(media::VideoFrame::kVPlane == 2, VideoFrame_kVPlane_mismatch);
119
120   TRACE_EVENT1("browser", "MapBufferToVideoFrame", "plane", plane);
121
122   // Apply black-out in the regions surrounding the view area (for
123   // letterboxing/pillarboxing).  Only do this once, since this is performed on
124   // all planes in the VideoFrame here.
125   if (plane == 0)
126     media::LetterboxYUV(target.get(), region_in_frame);
127
128   if (buf) {
129     int packed_width = region_in_frame.width();
130     int packed_height = region_in_frame.height();
131     // For planes 1 and 2, the width and height are 1/2 size (rounded up).
132     if (plane > 0) {
133       packed_width = (packed_width + 1) / 2;
134       packed_height = (packed_height + 1) / 2;
135     }
136     const uint8* src = reinterpret_cast<const uint8*>(buf);
137     const int src_stride = (packed_width % 4 == 0 ?
138                                 packed_width :
139                                 (packed_width + 4 - (packed_width % 4)));
140     const uint8* const src_end = src + packed_height * src_stride;
141
142     // Calculate starting offset and stride into the destination buffer.
143     const int dst_stride = target->stride(plane);
144     uint8* dst = target->data(plane);
145     if (plane == 0)
146       dst += (region_in_frame.y() * dst_stride) + region_in_frame.x();
147     else
148       dst += (region_in_frame.y() / 2 * dst_stride) + (region_in_frame.x() / 2);
149
150     // Copy each row, accounting for strides in the source and destination.
151     for (; src < src_end; src += src_stride, dst += dst_stride)
152       memcpy(dst, src, packed_width);
153   }
154   return buf != NULL;
155 }
156
157 }  // namespace
158
159 CompositingIOSurfaceMac::CopyContext::CopyContext(
160     const scoped_refptr<CompositingIOSurfaceContext>& context)
161   : transformer(new CompositingIOSurfaceTransformer(
162         GL_TEXTURE_RECTANGLE_ARB, true, context->shader_program_cache())),
163     output_readback_format(GL_BGRA),
164     num_outputs(0),
165     fence(0),
166     cycles_elapsed(0) {
167   memset(output_textures, 0, sizeof(output_textures));
168   memset(frame_buffers, 0, sizeof(frame_buffers));
169   memset(pixel_buffers, 0, sizeof(pixel_buffers));
170 }
171
172 CompositingIOSurfaceMac::CopyContext::~CopyContext() {
173   DCHECK_EQ(frame_buffers[0], 0u) << "Failed to call ReleaseCachedGLObjects().";
174 }
175
176 void CompositingIOSurfaceMac::CopyContext::ReleaseCachedGLObjects() {
177   // No outstanding callbacks should be pending.
178   DCHECK(map_buffer_callback.is_null());
179   DCHECK(done_callback.is_null());
180
181   // For an asynchronous read-back, there are more objects to delete:
182   if (fence) {
183     glDeleteBuffers(arraysize(pixel_buffers), pixel_buffers); CHECK_GL_ERROR();
184     memset(pixel_buffers, 0, sizeof(pixel_buffers));
185     glDeleteFencesAPPLE(1, &fence); CHECK_GL_ERROR();
186     fence = 0;
187   }
188
189   glDeleteFramebuffersEXT(arraysize(frame_buffers), frame_buffers);
190   CHECK_GL_ERROR();
191   memset(frame_buffers, 0, sizeof(frame_buffers));
192
193   // Note: |output_textures| are owned by the transformer.
194   if (transformer)
195     transformer->ReleaseCachedGLObjects();
196 }
197
198 void CompositingIOSurfaceMac::CopyContext::PrepareReadbackFramebuffers() {
199   for (int i = 0; i < num_outputs; ++i) {
200     if (!frame_buffers[i]) {
201       glGenFramebuffersEXT(1, &frame_buffers[i]); CHECK_GL_ERROR();
202     }
203   }
204 }
205
206 void CompositingIOSurfaceMac::CopyContext::PrepareForAsynchronousReadback() {
207   PrepareReadbackFramebuffers();
208   if (!fence) {
209     glGenFencesAPPLE(1, &fence); CHECK_GL_ERROR();
210   }
211   for (int i = 0; i < num_outputs; ++i) {
212     if (!pixel_buffers[i]) {
213       glGenBuffersARB(1, &pixel_buffers[i]); CHECK_GL_ERROR();
214     }
215   }
216 }
217
218
219 // static
220 scoped_refptr<CompositingIOSurfaceMac> CompositingIOSurfaceMac::Create() {
221   scoped_refptr<CompositingIOSurfaceContext> offscreen_context =
222       CompositingIOSurfaceContext::Get(
223           CompositingIOSurfaceContext::kOffscreenContextWindowNumber);
224   if (!offscreen_context) {
225     LOG(ERROR) << "Failed to create context for offscreen operations";
226     return NULL;
227   }
228
229   return new CompositingIOSurfaceMac(offscreen_context);
230 }
231
232 CompositingIOSurfaceMac::CompositingIOSurfaceMac(
233     const scoped_refptr<CompositingIOSurfaceContext>& offscreen_context)
234     : offscreen_context_(offscreen_context),
235       io_surface_handle_(0),
236       scale_factor_(1.f),
237       texture_(0),
238       finish_copy_timer_(
239           FROM_HERE,
240           base::TimeDelta::FromMilliseconds(kFinishCopyPollingPeriodMs),
241           base::Bind(&CompositingIOSurfaceMac::CheckIfAllCopiesAreFinished,
242                      base::Unretained(this),
243                      false),
244           true),
245       gl_error_(GL_NO_ERROR),
246       eviction_queue_iterator_(eviction_queue_.Get().end()),
247       eviction_has_been_drawn_since_updated_(false) {
248   CHECK(offscreen_context_);
249 }
250
251 CompositingIOSurfaceMac::~CompositingIOSurfaceMac() {
252   FailAllCopies();
253   {
254     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
255         offscreen_context_->cgl_context());
256     DestroyAllCopyContextsWithinContext();
257     UnrefIOSurfaceWithContextCurrent();
258   }
259   offscreen_context_ = NULL;
260   DCHECK(eviction_queue_iterator_ == eviction_queue_.Get().end());
261 }
262
263 bool CompositingIOSurfaceMac::SetIOSurfaceWithContextCurrent(
264     scoped_refptr<CompositingIOSurfaceContext> current_context,
265     IOSurfaceID io_surface_handle,
266     const gfx::Size& size,
267     float scale_factor) {
268   bool result = MapIOSurfaceToTextureWithContextCurrent(
269       current_context, size, scale_factor, io_surface_handle);
270   EvictionMarkUpdated();
271   return result;
272 }
273
274 int CompositingIOSurfaceMac::GetRendererID() {
275   GLint current_renderer_id = -1;
276   if (CGLGetParameter(offscreen_context_->cgl_context(),
277                       kCGLCPCurrentRendererID,
278                       &current_renderer_id) == kCGLNoError)
279     return current_renderer_id & kCGLRendererIDMatchingMask;
280   return -1;
281 }
282
283 bool CompositingIOSurfaceMac::DrawIOSurface(
284     scoped_refptr<CompositingIOSurfaceContext> drawing_context,
285     const gfx::Rect& window_rect,
286     float window_scale_factor) {
287   DCHECK_EQ(CGLGetCurrentContext(), drawing_context->cgl_context());
288
289   bool has_io_surface = HasIOSurface();
290   TRACE_EVENT1("browser", "CompositingIOSurfaceMac::DrawIOSurface",
291                "has_io_surface", has_io_surface);
292
293   gfx::Rect pixel_window_rect =
294       ToNearestRect(gfx::ScaleRect(window_rect, window_scale_factor));
295   glViewport(
296       pixel_window_rect.x(), pixel_window_rect.y(),
297       pixel_window_rect.width(), pixel_window_rect.height());
298
299   SurfaceQuad quad;
300   quad.set_size(dip_io_surface_size_, pixel_io_surface_size_);
301
302   glMatrixMode(GL_PROJECTION);
303   glLoadIdentity();
304
305   // Note that the projection keeps things in view units, so the use of
306   // window_rect / dip_io_surface_size_ (as opposed to the pixel_ variants)
307   // below is correct.
308   glOrtho(0, window_rect.width(), window_rect.height(), 0, -1, 1);
309   glMatrixMode(GL_MODELVIEW);
310   glLoadIdentity();
311
312   glDisable(GL_DEPTH_TEST);
313   glDisable(GL_BLEND);
314
315   if (has_io_surface) {
316     drawing_context->shader_program_cache()->UseBlitProgram();
317     glActiveTexture(GL_TEXTURE0);
318     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_);
319
320     DrawQuad(quad);
321
322     glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); CHECK_AND_SAVE_GL_ERROR();
323
324     // Fill the resize gutters with white.
325     if (window_rect.width() > dip_io_surface_size_.width() ||
326         window_rect.height() > dip_io_surface_size_.height()) {
327       drawing_context->shader_program_cache()->UseSolidWhiteProgram();
328       SurfaceQuad filler_quad;
329       if (window_rect.width() > dip_io_surface_size_.width()) {
330         // Draw right-side gutter down to the bottom of the window.
331         filler_quad.set_rect(dip_io_surface_size_.width(), 0.0f,
332                              window_rect.width(), window_rect.height());
333         DrawQuad(filler_quad);
334       }
335       if (window_rect.height() > dip_io_surface_size_.height()) {
336         // Draw bottom gutter to the width of the IOSurface.
337         filler_quad.set_rect(
338             0.0f, dip_io_surface_size_.height(),
339             dip_io_surface_size_.width(), window_rect.height());
340         DrawQuad(filler_quad);
341       }
342     }
343
344     // Workaround for issue 158469. Issue a dummy draw call with texture_ not
345     // bound to blit_rgb_sampler_location_, in order to shake all references
346     // to the IOSurface out of the driver.
347     glBegin(GL_TRIANGLES);
348     glEnd();
349
350     glUseProgram(0); CHECK_AND_SAVE_GL_ERROR();
351   } else {
352     // Should match the clear color of RenderWidgetHostViewMac.
353     glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
354     glClear(GL_COLOR_BUFFER_BIT);
355   }
356
357   bool workaround_needed =
358       GpuDataManagerImpl::GetInstance()->IsDriverBugWorkaroundActive(
359           gpu::FORCE_GL_FINISH_AFTER_COMPOSITING);
360   if (workaround_needed) {
361     TRACE_EVENT0("gpu", "glFinish");
362     glFinish();
363   }
364
365   // Check if any of the drawing calls result in an error.
366   GetAndSaveGLError();
367   bool result = true;
368   if (gl_error_ != GL_NO_ERROR) {
369     LOG(ERROR) << "GL error in DrawIOSurface: " << gl_error_;
370     result = false;
371     // If there was an error, clear the screen to a light grey to avoid
372     // rendering artifacts. If we're in a really bad way, this too may
373     // generate an error. Clear the GL error afterwards just in case.
374     glClearColor(0.8, 0.8, 0.8, 1.0);
375     glClear(GL_COLOR_BUFFER_BIT);
376     glGetError();
377   }
378
379   eviction_has_been_drawn_since_updated_ = true;
380   return result;
381 }
382
383 void CompositingIOSurfaceMac::CopyTo(
384       const gfx::Rect& src_pixel_subrect,
385       const gfx::Size& dst_pixel_size,
386       const base::Callback<void(bool, const SkBitmap&)>& callback) {
387   scoped_ptr<SkBitmap> output(new SkBitmap());
388   if (!output->allocN32Pixels(
389           dst_pixel_size.width(), dst_pixel_size.height(), true)) {
390     DLOG(ERROR) << "Failed to allocate SkBitmap pixels!";
391     callback.Run(false, *output);
392     return;
393   }
394   DCHECK_EQ(output->rowBytesAsPixels(), dst_pixel_size.width())
395       << "Stride is required to be equal to width for GPU readback.";
396
397   base::Closure copy_done_callback;
398   {
399     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
400         offscreen_context_->cgl_context());
401     copy_done_callback = CopyToSelectedOutputWithinContext(
402         src_pixel_subrect, gfx::Rect(dst_pixel_size), false,
403         output.get(), NULL,
404         base::Bind(&ReverseArgumentOrder, callback, base::Passed(&output)));
405   }
406   if (!copy_done_callback.is_null())
407     copy_done_callback.Run();
408 }
409
410 void CompositingIOSurfaceMac::CopyToVideoFrame(
411     const gfx::Rect& src_pixel_subrect,
412     const scoped_refptr<media::VideoFrame>& target,
413     const base::Callback<void(bool)>& callback) {
414   base::Closure copy_done_callback;
415   {
416     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
417         offscreen_context_->cgl_context());
418     copy_done_callback = CopyToVideoFrameWithinContext(
419         src_pixel_subrect, false, target, callback);
420   }
421   if (!copy_done_callback.is_null())
422     copy_done_callback.Run();
423 }
424
425 base::Closure CompositingIOSurfaceMac::CopyToVideoFrameWithinContext(
426     const gfx::Rect& src_pixel_subrect,
427     bool called_within_draw,
428     const scoped_refptr<media::VideoFrame>& target,
429     const base::Callback<void(bool)>& callback) {
430   gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
431       gfx::Rect(target->coded_size()), src_pixel_subrect.size());
432   // Make coordinates and sizes even because we letterbox in YUV space right
433   // now (see CopyRGBToVideoFrame). They need to be even for the UV samples to
434   // line up correctly.
435   region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
436                               region_in_frame.y() & ~1,
437                               region_in_frame.width() & ~1,
438                               region_in_frame.height() & ~1);
439   DCHECK_LE(region_in_frame.right(), target->coded_size().width());
440   DCHECK_LE(region_in_frame.bottom(), target->coded_size().height());
441
442   return CopyToSelectedOutputWithinContext(
443       src_pixel_subrect, region_in_frame, called_within_draw,
444       NULL, target, callback);
445 }
446
447 bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent(
448     const scoped_refptr<CompositingIOSurfaceContext>& current_context,
449     const gfx::Size pixel_size,
450     float scale_factor,
451     IOSurfaceID io_surface_handle) {
452   TRACE_EVENT0("browser", "CompositingIOSurfaceMac::MapIOSurfaceToTexture");
453
454   if (!io_surface_ || io_surface_handle != io_surface_handle_)
455     UnrefIOSurfaceWithContextCurrent();
456
457   pixel_io_surface_size_ = pixel_size;
458   scale_factor_ = scale_factor;
459   dip_io_surface_size_ = gfx::ToFlooredSize(
460       gfx::ScaleSize(pixel_io_surface_size_, 1.0 / scale_factor_));
461
462   // Early-out if the IOSurface has not changed. Note that because IOSurface
463   // sizes are rounded, the same IOSurface may have two different sizes
464   // associated with it.
465   if (io_surface_ && io_surface_handle == io_surface_handle_)
466     return true;
467
468   io_surface_.reset(IOSurfaceLookup(io_surface_handle));
469   // Can fail if IOSurface with that ID was already released by the gpu
470   // process.
471   if (!io_surface_) {
472     UnrefIOSurfaceWithContextCurrent();
473     return false;
474   }
475
476   io_surface_handle_ = io_surface_handle;
477
478   // Actual IOSurface size is rounded up to reduce reallocations during window
479   // resize. Get the actual size to properly map the texture.
480   gfx::Size rounded_size(IOSurfaceGetWidth(io_surface_),
481                          IOSurfaceGetHeight(io_surface_));
482
483   glGenTextures(1, &texture_);
484   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_);
485   glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
486   glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
487   CHECK_AND_SAVE_GL_ERROR();
488   GLuint plane = 0;
489   CGLError cgl_error = CGLTexImageIOSurface2D(
490       current_context->cgl_context(),
491       GL_TEXTURE_RECTANGLE_ARB,
492       GL_RGBA,
493       rounded_size.width(),
494       rounded_size.height(),
495       GL_BGRA,
496       GL_UNSIGNED_INT_8_8_8_8_REV,
497       io_surface_.get(),
498       plane);
499   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
500   if (cgl_error != kCGLNoError) {
501     LOG(ERROR) << "CGLTexImageIOSurface2D: " << cgl_error;
502     UnrefIOSurfaceWithContextCurrent();
503     return false;
504   }
505   GetAndSaveGLError();
506   if (gl_error_ != GL_NO_ERROR) {
507     LOG(ERROR) << "GL error in MapIOSurfaceToTexture: " << gl_error_;
508     UnrefIOSurfaceWithContextCurrent();
509     return false;
510   }
511   return true;
512 }
513
514 void CompositingIOSurfaceMac::UnrefIOSurface() {
515   gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
516       offscreen_context_->cgl_context());
517   UnrefIOSurfaceWithContextCurrent();
518 }
519
520 void CompositingIOSurfaceMac::DrawQuad(const SurfaceQuad& quad) {
521   TRACE_EVENT0("gpu", "CompositingIOSurfaceMac::DrawQuad");
522
523   glEnableClientState(GL_VERTEX_ARRAY); CHECK_AND_SAVE_GL_ERROR();
524   glEnableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_AND_SAVE_GL_ERROR();
525
526   glVertexPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &quad.verts_[0].x_);
527   glTexCoordPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &quad.verts_[0].tx_);
528   glDrawArrays(GL_QUADS, 0, 4); CHECK_AND_SAVE_GL_ERROR();
529
530   glDisableClientState(GL_VERTEX_ARRAY);
531   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
532 }
533
534 void CompositingIOSurfaceMac::UnrefIOSurfaceWithContextCurrent() {
535   if (texture_) {
536     glDeleteTextures(1, &texture_);
537     texture_ = 0;
538   }
539   pixel_io_surface_size_ = gfx::Size();
540   scale_factor_ = 1;
541   dip_io_surface_size_ = gfx::Size();
542   io_surface_.reset();
543
544   // Forget the ID, because even if it is still around when we want to use it
545   // again, OSX may have reused the same ID for a new tab and we don't want to
546   // blit random tab contents.
547   io_surface_handle_ = 0;
548
549   EvictionMarkEvicted();
550 }
551
552 bool CompositingIOSurfaceMac::IsAsynchronousReadbackSupported() {
553   if (!HasAppleFenceExtension() && HasPixelBufferObjectExtension())
554     return false;
555   if (GpuDataManagerImpl::GetInstance()->IsDriverBugWorkaroundActive(
556           gpu::DISABLE_ASYNC_READPIXELS)) {
557     return false;
558   }
559   return true;
560 }
561
562 bool CompositingIOSurfaceMac::HasBeenPoisoned() const {
563   return offscreen_context_->HasBeenPoisoned();
564 }
565
566 base::Closure CompositingIOSurfaceMac::CopyToSelectedOutputWithinContext(
567     const gfx::Rect& src_pixel_subrect,
568     const gfx::Rect& dst_pixel_rect,
569     bool called_within_draw,
570     const SkBitmap* bitmap_output,
571     const scoped_refptr<media::VideoFrame>& video_frame_output,
572     const base::Callback<void(bool)>& done_callback) {
573   DCHECK_NE(bitmap_output != NULL, video_frame_output.get() != NULL);
574   DCHECK(!done_callback.is_null());
575
576   // SWIZZLE_RGBA_FOR_ASYNC_READPIXELS workaround: Fall-back to synchronous
577   // readback for SkBitmap output since the Blit shader program doesn't support
578   // switchable output formats.
579   const bool require_sync_copy_for_workaround = bitmap_output &&
580       offscreen_context_->shader_program_cache()->rgb_to_yv12_output_format() ==
581           GL_RGBA;
582   const bool async_copy = !require_sync_copy_for_workaround &&
583       IsAsynchronousReadbackSupported();
584   TRACE_EVENT2(
585       "browser", "CompositingIOSurfaceMac::CopyToSelectedOutputWithinContext",
586       "output", bitmap_output ? "SkBitmap (ARGB)" : "VideoFrame (YV12)",
587       "async_readback", async_copy);
588
589   const gfx::Rect src_rect = IntersectWithIOSurface(src_pixel_subrect);
590   if (src_rect.IsEmpty() || dst_pixel_rect.IsEmpty())
591     return base::Bind(done_callback, false);
592
593   CopyContext* copy_context;
594   if (copy_context_pool_.empty()) {
595     // Limit the maximum number of simultaneous copies to two.  Rationale:
596     // Really, only one should ever be in-progress at a time, as we should
597     // depend on the speed of the hardware to rate-limit the copying naturally.
598     // In the asynchronous read-back case, the one currently in-flight copy is
599     // highly likely to have finished by this point (i.e., it's just waiting for
600     // us to make a glMapBuffer() call).  Therefore, we allow a second copy to
601     // be started here.
602     if (copy_requests_.size() >= 2)
603       return base::Bind(done_callback, false);
604     copy_context = new CopyContext(offscreen_context_);
605   } else {
606     copy_context = copy_context_pool_.back();
607     copy_context_pool_.pop_back();
608   }
609
610   if (!HasIOSurface())
611     return base::Bind(done_callback, false);
612
613   // Send transform commands to the GPU.
614   copy_context->num_outputs = 0;
615   if (bitmap_output) {
616     if (copy_context->transformer->ResizeBilinear(
617             texture_, src_rect, dst_pixel_rect.size(),
618             &copy_context->output_textures[0])) {
619       copy_context->output_readback_format = GL_BGRA;
620       copy_context->num_outputs = 1;
621       copy_context->output_texture_sizes[0] = dst_pixel_rect.size();
622     }
623   } else {
624     if (copy_context->transformer->TransformRGBToYV12(
625             texture_, src_rect, dst_pixel_rect.size(),
626             &copy_context->output_textures[0],
627             &copy_context->output_textures[1],
628             &copy_context->output_textures[2],
629             &copy_context->output_texture_sizes[0],
630             &copy_context->output_texture_sizes[1])) {
631       copy_context->output_readback_format =
632           offscreen_context_->shader_program_cache()->
633               rgb_to_yv12_output_format();
634       copy_context->num_outputs = 3;
635       copy_context->output_texture_sizes[2] =
636           copy_context->output_texture_sizes[1];
637     }
638   }
639   if (!copy_context->num_outputs)
640     return base::Bind(done_callback, false);
641
642   // In the asynchronous case, issue commands to the GPU and return a null
643   // closure here.  In the synchronous case, perform a blocking readback and
644   // return a callback to be run outside the CGL context to indicate success.
645   if (async_copy) {
646     copy_context->done_callback = done_callback;
647     AsynchronousReadbackForCopy(
648         dst_pixel_rect, called_within_draw, copy_context, bitmap_output,
649         video_frame_output);
650     copy_requests_.push_back(copy_context);
651     if (!finish_copy_timer_.IsRunning())
652       finish_copy_timer_.Reset();
653     return base::Closure();
654   } else {
655     const bool success = SynchronousReadbackForCopy(
656         dst_pixel_rect, copy_context, bitmap_output, video_frame_output);
657     return base::Bind(done_callback, success);
658   }
659 }
660
661 void CompositingIOSurfaceMac::AsynchronousReadbackForCopy(
662     const gfx::Rect& dst_pixel_rect,
663     bool called_within_draw,
664     CopyContext* copy_context,
665     const SkBitmap* bitmap_output,
666     const scoped_refptr<media::VideoFrame>& video_frame_output) {
667   copy_context->PrepareForAsynchronousReadback();
668
669   // Copy the textures to their corresponding PBO.
670   for (int i = 0; i < copy_context->num_outputs; ++i) {
671     TRACE_EVENT1(
672         "browser", "CompositingIOSurfaceMac::AsynchronousReadbackForCopy",
673         "plane", i);
674
675     // Attach the output texture to the FBO.
676     glBindFramebufferEXT(
677         GL_READ_FRAMEBUFFER_EXT, copy_context->frame_buffers[i]);
678     glFramebufferTexture2DEXT(
679         GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
680         GL_TEXTURE_RECTANGLE_ARB, copy_context->output_textures[i], 0);
681     DCHECK(glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT) ==
682                GL_FRAMEBUFFER_COMPLETE_EXT);
683
684     // Create a PBO and issue an asynchronous read-back.
685     glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, copy_context->pixel_buffers[i]);
686     CHECK_AND_SAVE_GL_ERROR();
687     glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB,
688                     copy_context->output_texture_sizes[i].GetArea() * 4,
689                     NULL, GL_STREAM_READ_ARB);
690     CHECK_AND_SAVE_GL_ERROR();
691     glReadPixels(0, 0,
692                  copy_context->output_texture_sizes[i].width(),
693                  copy_context->output_texture_sizes[i].height(),
694                  copy_context->output_readback_format,
695                  GL_UNSIGNED_INT_8_8_8_8_REV, 0);
696     CHECK_AND_SAVE_GL_ERROR();
697   }
698
699   glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); CHECK_AND_SAVE_GL_ERROR();
700   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); CHECK_AND_SAVE_GL_ERROR();
701
702   glSetFenceAPPLE(copy_context->fence); CHECK_GL_ERROR();
703   copy_context->cycles_elapsed = 0;
704
705   // When this asynchronous copy happens in a draw operaton there is no need
706   // to explicitly flush because there will be a swap buffer and this flush
707   // hurts performance.
708   if (!called_within_draw) {
709     glFlush(); CHECK_AND_SAVE_GL_ERROR();
710   }
711
712   copy_context->map_buffer_callback = bitmap_output ?
713       base::Bind(&MapBufferToSkBitmap, bitmap_output) :
714       base::Bind(&MapBufferToVideoFrame, video_frame_output, dst_pixel_rect);
715 }
716
717 void CompositingIOSurfaceMac::CheckIfAllCopiesAreFinished(
718     bool block_until_finished) {
719   if (copy_requests_.empty())
720     return;
721
722   std::vector<base::Closure> done_callbacks;
723   {
724     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
725         offscreen_context_->cgl_context());
726     CheckIfAllCopiesAreFinishedWithinContext(
727         block_until_finished, &done_callbacks);
728   }
729   for (size_t i = 0; i < done_callbacks.size(); ++i)
730     done_callbacks[i].Run();
731 }
732
733 void CompositingIOSurfaceMac::CheckIfAllCopiesAreFinishedWithinContext(
734     bool block_until_finished,
735     std::vector<base::Closure>* done_callbacks) {
736   while (!copy_requests_.empty()) {
737     CopyContext* const copy_context = copy_requests_.front();
738
739     if (copy_context->fence && !glTestFenceAPPLE(copy_context->fence)) {
740       CHECK_AND_SAVE_GL_ERROR();
741       // Doing a glFinishFenceAPPLE can cause transparent window flashes when
742       // switching tabs, so only do it when required.
743       if (block_until_finished) {
744         glFinishFenceAPPLE(copy_context->fence);
745         CHECK_AND_SAVE_GL_ERROR();
746       } else if (copy_context->cycles_elapsed < kFinishCopyRetryCycles) {
747         ++copy_context->cycles_elapsed;
748         // This copy has not completed there is no need to test subsequent
749         // requests.
750         break;
751       }
752     }
753     CHECK_AND_SAVE_GL_ERROR();
754
755     bool success = true;
756     for (int i = 0; success && i < copy_context->num_outputs; ++i) {
757       TRACE_EVENT1(
758         "browser",
759         "CompositingIOSurfaceMac::CheckIfAllCopiesAreFinishedWithinContext",
760         "plane", i);
761
762       glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, copy_context->pixel_buffers[i]);
763       CHECK_AND_SAVE_GL_ERROR();
764
765       void* buf = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
766       CHECK_AND_SAVE_GL_ERROR();
767       success &= copy_context->map_buffer_callback.Run(buf, i);
768       glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); CHECK_AND_SAVE_GL_ERROR();
769     }
770     copy_context->map_buffer_callback.Reset();
771     glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); CHECK_AND_SAVE_GL_ERROR();
772
773     copy_requests_.pop_front();
774     done_callbacks->push_back(base::Bind(copy_context->done_callback, success));
775     copy_context->done_callback.Reset();
776     copy_context_pool_.push_back(copy_context);
777   }
778   if (copy_requests_.empty())
779     finish_copy_timer_.Stop();
780
781   CHECK(copy_requests_.empty() || !block_until_finished);
782 }
783
784 bool CompositingIOSurfaceMac::SynchronousReadbackForCopy(
785     const gfx::Rect& dst_pixel_rect,
786     CopyContext* copy_context,
787     const SkBitmap* bitmap_output,
788     const scoped_refptr<media::VideoFrame>& video_frame_output) {
789   bool success = true;
790   copy_context->PrepareReadbackFramebuffers();
791   for (int i = 0; i < copy_context->num_outputs; ++i) {
792     TRACE_EVENT1(
793         "browser", "CompositingIOSurfaceMac::SynchronousReadbackForCopy",
794         "plane", i);
795
796     // Attach the output texture to the FBO.
797     glBindFramebufferEXT(
798         GL_READ_FRAMEBUFFER_EXT, copy_context->frame_buffers[i]);
799     glFramebufferTexture2DEXT(
800         GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
801         GL_TEXTURE_RECTANGLE_ARB, copy_context->output_textures[i], 0);
802     DCHECK(glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT) ==
803                GL_FRAMEBUFFER_COMPLETE_EXT);
804
805     // Blocking read-back of pixels from textures.
806     void* buf;
807     // When data must be transferred into a VideoFrame one scanline at a time,
808     // it is necessary to allocate a separate buffer for glReadPixels() that can
809     // be populated one-shot.
810     //
811     // TODO(miu): Don't keep allocating/deleting this buffer for every frame.
812     // Keep it cached, allocated on first use.
813     scoped_ptr<uint32[]> temp_readback_buffer;
814     if (bitmap_output) {
815       // The entire SkBitmap is populated, never a region within.  So, read the
816       // texture directly into the bitmap's pixel memory.
817       buf = bitmap_output->getPixels();
818     } else {
819       // Optimization: If the VideoFrame is letterboxed (not pillarboxed), and
820       // its stride is equal to the stride of the data being read back, then
821       // readback directly into the VideoFrame's buffer to save a round of
822       // memcpy'ing.
823       //
824       // TODO(miu): Move these calculations into VideoFrame (need a CalcOffset()
825       // method).  http://crbug.com/219779
826       const int src_stride = copy_context->output_texture_sizes[i].width() * 4;
827       const int dst_stride = video_frame_output->stride(i);
828       if (src_stride == dst_stride && dst_pixel_rect.x() == 0) {
829         const int y_offset = dst_pixel_rect.y() / (i == 0 ? 1 : 2);
830         buf = video_frame_output->data(i) + y_offset * dst_stride;
831       } else {
832         // Create and readback into a temporary buffer because the data must be
833         // transferred to VideoFrame's pixel memory one scanline at a time.
834         temp_readback_buffer.reset(
835             new uint32[copy_context->output_texture_sizes[i].GetArea()]);
836         buf = temp_readback_buffer.get();
837       }
838     }
839     glReadPixels(0, 0,
840                  copy_context->output_texture_sizes[i].width(),
841                  copy_context->output_texture_sizes[i].height(),
842                  copy_context->output_readback_format,
843                  GL_UNSIGNED_INT_8_8_8_8_REV, buf);
844     CHECK_AND_SAVE_GL_ERROR();
845     if (video_frame_output.get()) {
846       if (!temp_readback_buffer) {
847         // Apply letterbox black-out around view region.
848         media::LetterboxYUV(video_frame_output.get(), dst_pixel_rect);
849       } else {
850         // Copy from temporary buffer and fully render the VideoFrame.
851         success &= MapBufferToVideoFrame(video_frame_output, dst_pixel_rect,
852                                          temp_readback_buffer.get(), i);
853       }
854     }
855   }
856
857   glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); CHECK_AND_SAVE_GL_ERROR();
858   copy_context_pool_.push_back(copy_context);
859   return success;
860 }
861
862 void CompositingIOSurfaceMac::FailAllCopies() {
863   for (size_t i = 0; i < copy_requests_.size(); ++i) {
864     copy_requests_[i]->map_buffer_callback.Reset();
865
866     base::Callback<void(bool)>& done_callback =
867         copy_requests_[i]->done_callback;
868     if (!done_callback.is_null()) {
869       done_callback.Run(false);
870       done_callback.Reset();
871     }
872   }
873 }
874
875 void CompositingIOSurfaceMac::DestroyAllCopyContextsWithinContext() {
876   // Move all in-flight copies, if any, back into the pool.  Then, destroy all
877   // the CopyContexts in the pool.
878   copy_context_pool_.insert(copy_context_pool_.end(),
879                             copy_requests_.begin(), copy_requests_.end());
880   copy_requests_.clear();
881   while (!copy_context_pool_.empty()) {
882     scoped_ptr<CopyContext> copy_context(copy_context_pool_.back());
883     copy_context_pool_.pop_back();
884     copy_context->ReleaseCachedGLObjects();
885   }
886 }
887
888 gfx::Rect CompositingIOSurfaceMac::IntersectWithIOSurface(
889     const gfx::Rect& rect) const {
890   return gfx::IntersectRects(rect,
891       gfx::ToEnclosingRect(gfx::Rect(pixel_io_surface_size_)));
892 }
893
894 GLenum CompositingIOSurfaceMac::GetAndSaveGLError() {
895   GLenum gl_error = glGetError();
896   if (gl_error_ == GL_NO_ERROR)
897     gl_error_ = gl_error;
898   return gl_error;
899 }
900
901 void CompositingIOSurfaceMac::EvictionMarkUpdated() {
902   EvictionMarkEvicted();
903   eviction_queue_.Get().push_back(this);
904   eviction_queue_iterator_ = --eviction_queue_.Get().end();
905   eviction_has_been_drawn_since_updated_ = false;
906   EvictionScheduleDoEvict();
907 }
908
909 void CompositingIOSurfaceMac::EvictionMarkEvicted() {
910   if (eviction_queue_iterator_ == eviction_queue_.Get().end())
911     return;
912   eviction_queue_.Get().erase(eviction_queue_iterator_);
913   eviction_queue_iterator_ = eviction_queue_.Get().end();
914   eviction_has_been_drawn_since_updated_ = false;
915 }
916
917 // static
918 void CompositingIOSurfaceMac::EvictionScheduleDoEvict() {
919   if (eviction_scheduled_)
920     return;
921   if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces)
922     return;
923
924   eviction_scheduled_ = true;
925   base::MessageLoop::current()->PostTask(
926       FROM_HERE,
927       base::Bind(&CompositingIOSurfaceMac::EvictionDoEvict));
928 }
929
930 // static
931 void CompositingIOSurfaceMac::EvictionDoEvict() {
932   eviction_scheduled_ = false;
933   // Walk the list of allocated surfaces from least recently used to most
934   // recently used.
935   for (EvictionQueue::iterator it = eviction_queue_.Get().begin();
936        it != eviction_queue_.Get().end();) {
937     CompositingIOSurfaceMac* surface = *it;
938     ++it;
939
940     // If the number of IOSurfaces allocated is less than the threshold,
941     // stop walking the list of surfaces.
942     if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces)
943       break;
944
945     // Don't evict anything that has not yet been drawn.
946     if (!surface->eviction_has_been_drawn_since_updated_)
947       continue;
948
949     // Don't evict anything with pending copy requests.
950     if (!surface->copy_requests_.empty())
951       continue;
952
953     // Evict the surface.
954     surface->UnrefIOSurface();
955   }
956 }
957
958 // static
959 base::LazyInstance<CompositingIOSurfaceMac::EvictionQueue>
960     CompositingIOSurfaceMac::eviction_queue_;
961
962 // static
963 bool CompositingIOSurfaceMac::eviction_scheduled_ = false;
964
965 }  // namespace content