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