OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/compositing_iosurface_mac.h" | 5 #include "content/browser/renderer_host/compositing_iosurface_mac.h" |
6 | 6 |
7 #include <OpenGL/CGLIOSurface.h> | 7 #include <OpenGL/CGLIOSurface.h> |
8 #include <OpenGL/CGLRenderers.h> | 8 #include <OpenGL/CGLRenderers.h> |
9 #include <OpenGL/OpenGL.h> | 9 #include <OpenGL/OpenGL.h> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
13 #include "base/debug/trace_event.h" | 13 #include "base/debug/trace_event.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/mac/mac_util.h" | 15 #include "base/mac/mac_util.h" |
16 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
17 #include "base/threading/platform_thread.h" | 17 #include "base/threading/platform_thread.h" |
18 #include "content/browser/gpu/gpu_data_manager_impl.h" | 18 #include "content/browser/gpu/gpu_data_manager_impl.h" |
19 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h" | 19 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h" |
| 20 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_ma
c.h" |
| 21 #include "content/browser/renderer_host/compositing_iosurface_transformer_mac.h" |
20 #include "content/browser/renderer_host/render_widget_host_impl.h" | 22 #include "content/browser/renderer_host/render_widget_host_impl.h" |
21 #include "content/browser/renderer_host/render_widget_host_view_mac.h" | 23 #include "content/browser/renderer_host/render_widget_host_view_mac.h" |
22 #include "content/common/content_constants_internal.h" | 24 #include "content/common/content_constants_internal.h" |
23 #include "gpu/config/gpu_driver_bug_workaround_type.h" | 25 #include "gpu/config/gpu_driver_bug_workaround_type.h" |
24 #include "media/base/video_util.h" | 26 #include "media/base/video_util.h" |
25 #include "third_party/skia/include/core/SkBitmap.h" | 27 #include "third_party/skia/include/core/SkBitmap.h" |
26 #include "ui/gfx/rect.h" | 28 #include "ui/gfx/rect.h" |
27 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | 29 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" |
28 #include "ui/gfx/size_conversions.h" | 30 #include "ui/gfx/size_conversions.h" |
29 #include "ui/gl/gl_context.h" | 31 #include "ui/gl/gl_context.h" |
30 | 32 |
31 #ifdef NDEBUG | 33 #ifdef NDEBUG |
32 #define CHECK_GL_ERROR() | 34 #define CHECK_GL_ERROR() |
33 #define CHECK_AND_SAVE_GL_ERROR() | 35 #define CHECK_AND_SAVE_GL_ERROR() |
34 #else | 36 #else |
35 #define CHECK_GL_ERROR() do { \ | 37 #define CHECK_GL_ERROR() do { \ |
36 GLenum gl_error = glGetError(); \ | 38 GLenum gl_error = glGetError(); \ |
37 LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \ | 39 LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \ |
38 } while (0) | 40 } while (0) |
39 #define CHECK_AND_SAVE_GL_ERROR() do { \ | 41 #define CHECK_AND_SAVE_GL_ERROR() do { \ |
40 GLenum gl_error = GetAndSaveGLError(); \ | 42 GLenum gl_error = GetAndSaveGLError(); \ |
41 LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \ | 43 LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \ |
42 } while (0) | 44 } while (0) |
43 #endif | 45 #endif |
44 | 46 |
45 namespace content { | 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 |
46 | 218 |
47 // static | 219 // static |
48 scoped_refptr<CompositingIOSurfaceMac> CompositingIOSurfaceMac::Create() { | 220 scoped_refptr<CompositingIOSurfaceMac> CompositingIOSurfaceMac::Create() { |
49 scoped_refptr<CompositingIOSurfaceContext> offscreen_context = | 221 scoped_refptr<CompositingIOSurfaceContext> offscreen_context = |
50 CompositingIOSurfaceContext::Get( | 222 CompositingIOSurfaceContext::Get( |
51 CompositingIOSurfaceContext::kOffscreenContextWindowNumber); | 223 CompositingIOSurfaceContext::kOffscreenContextWindowNumber); |
52 if (!offscreen_context) { | 224 if (!offscreen_context) { |
53 LOG(ERROR) << "Failed to create context for offscreen operations"; | 225 LOG(ERROR) << "Failed to create context for offscreen operations"; |
54 return NULL; | 226 return NULL; |
55 } | 227 } |
56 | 228 |
57 return new CompositingIOSurfaceMac(offscreen_context); | 229 return new CompositingIOSurfaceMac(offscreen_context); |
58 } | 230 } |
59 | 231 |
60 CompositingIOSurfaceMac::CompositingIOSurfaceMac( | 232 CompositingIOSurfaceMac::CompositingIOSurfaceMac( |
61 const scoped_refptr<CompositingIOSurfaceContext>& offscreen_context) | 233 const scoped_refptr<CompositingIOSurfaceContext>& offscreen_context) |
62 : offscreen_context_(offscreen_context), | 234 : offscreen_context_(offscreen_context), |
63 io_surface_handle_(0), | 235 io_surface_handle_(0), |
64 scale_factor_(1.f), | 236 scale_factor_(1.f), |
65 texture_(0), | 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), |
66 gl_error_(GL_NO_ERROR), | 245 gl_error_(GL_NO_ERROR), |
67 eviction_queue_iterator_(eviction_queue_.Get().end()), | 246 eviction_queue_iterator_(eviction_queue_.Get().end()), |
68 eviction_has_been_drawn_since_updated_(false) { | 247 eviction_has_been_drawn_since_updated_(false) { |
69 CHECK(offscreen_context_); | 248 CHECK(offscreen_context_); |
70 } | 249 } |
71 | 250 |
72 CompositingIOSurfaceMac::~CompositingIOSurfaceMac() { | 251 CompositingIOSurfaceMac::~CompositingIOSurfaceMac() { |
| 252 FailAllCopies(); |
73 { | 253 { |
74 gfx::ScopedCGLSetCurrentContext scoped_set_current_context( | 254 gfx::ScopedCGLSetCurrentContext scoped_set_current_context( |
75 offscreen_context_->cgl_context()); | 255 offscreen_context_->cgl_context()); |
| 256 DestroyAllCopyContextsWithinContext(); |
76 UnrefIOSurfaceWithContextCurrent(); | 257 UnrefIOSurfaceWithContextCurrent(); |
77 } | 258 } |
78 offscreen_context_ = NULL; | 259 offscreen_context_ = NULL; |
79 DCHECK(eviction_queue_iterator_ == eviction_queue_.Get().end()); | 260 DCHECK(eviction_queue_iterator_ == eviction_queue_.Get().end()); |
80 } | 261 } |
81 | 262 |
82 bool CompositingIOSurfaceMac::SetIOSurfaceWithContextCurrent( | 263 bool CompositingIOSurfaceMac::SetIOSurfaceWithContextCurrent( |
83 scoped_refptr<CompositingIOSurfaceContext> current_context, | 264 scoped_refptr<CompositingIOSurfaceContext> current_context, |
84 IOSurfaceID io_surface_handle, | 265 IOSurfaceID io_surface_handle, |
85 const gfx::Size& size, | 266 const gfx::Size& size, |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 // Note that the projection keeps things in view units, so the use of | 305 // Note that the projection keeps things in view units, so the use of |
125 // window_rect / dip_io_surface_size_ (as opposed to the pixel_ variants) | 306 // window_rect / dip_io_surface_size_ (as opposed to the pixel_ variants) |
126 // below is correct. | 307 // below is correct. |
127 glOrtho(0, window_rect.width(), window_rect.height(), 0, -1, 1); | 308 glOrtho(0, window_rect.width(), window_rect.height(), 0, -1, 1); |
128 glMatrixMode(GL_MODELVIEW); | 309 glMatrixMode(GL_MODELVIEW); |
129 glLoadIdentity(); | 310 glLoadIdentity(); |
130 | 311 |
131 glDisable(GL_DEPTH_TEST); | 312 glDisable(GL_DEPTH_TEST); |
132 glDisable(GL_BLEND); | 313 glDisable(GL_BLEND); |
133 | 314 |
134 glColor4f(1, 1, 1, 1); | |
135 if (has_io_surface) { | 315 if (has_io_surface) { |
136 glEnable(GL_TEXTURE_RECTANGLE_ARB); | 316 drawing_context->shader_program_cache()->UseBlitProgram(); |
| 317 glActiveTexture(GL_TEXTURE0); |
137 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_); | 318 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_); |
| 319 |
138 DrawQuad(quad); | 320 DrawQuad(quad); |
139 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | 321 |
140 glDisable(GL_TEXTURE_RECTANGLE_ARB); | 322 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); CHECK_AND_SAVE_GL_ERROR(); |
141 CHECK_AND_SAVE_GL_ERROR(); | |
142 | 323 |
143 // Fill the resize gutters with white. | 324 // Fill the resize gutters with white. |
144 if (window_rect.width() > dip_io_surface_size_.width() || | 325 if (window_rect.width() > dip_io_surface_size_.width() || |
145 window_rect.height() > dip_io_surface_size_.height()) { | 326 window_rect.height() > dip_io_surface_size_.height()) { |
| 327 drawing_context->shader_program_cache()->UseSolidWhiteProgram(); |
146 SurfaceQuad filler_quad; | 328 SurfaceQuad filler_quad; |
147 if (window_rect.width() > dip_io_surface_size_.width()) { | 329 if (window_rect.width() > dip_io_surface_size_.width()) { |
148 // Draw right-side gutter down to the bottom of the window. | 330 // Draw right-side gutter down to the bottom of the window. |
149 filler_quad.set_rect(dip_io_surface_size_.width(), 0.0f, | 331 filler_quad.set_rect(dip_io_surface_size_.width(), 0.0f, |
150 window_rect.width(), window_rect.height()); | 332 window_rect.width(), window_rect.height()); |
151 DrawQuad(filler_quad); | 333 DrawQuad(filler_quad); |
152 } | 334 } |
153 if (window_rect.height() > dip_io_surface_size_.height()) { | 335 if (window_rect.height() > dip_io_surface_size_.height()) { |
154 // Draw bottom gutter to the width of the IOSurface. | 336 // Draw bottom gutter to the width of the IOSurface. |
155 filler_quad.set_rect( | 337 filler_quad.set_rect( |
156 0.0f, dip_io_surface_size_.height(), | 338 0.0f, dip_io_surface_size_.height(), |
157 dip_io_surface_size_.width(), window_rect.height()); | 339 dip_io_surface_size_.width(), window_rect.height()); |
158 DrawQuad(filler_quad); | 340 DrawQuad(filler_quad); |
159 } | 341 } |
160 } | 342 } |
161 | 343 |
162 // Workaround for issue 158469. Issue a dummy draw call with texture_ not | 344 // Workaround for issue 158469. Issue a dummy draw call with texture_ not |
163 // bound to a texture, in order to shake all references to the IOSurface out | 345 // bound to blit_rgb_sampler_location_, in order to shake all references |
164 // of the driver. | 346 // to the IOSurface out of the driver. |
165 glBegin(GL_TRIANGLES); | 347 glBegin(GL_TRIANGLES); |
166 glEnd(); | 348 glEnd(); |
167 CHECK_AND_SAVE_GL_ERROR(); | 349 |
| 350 glUseProgram(0); CHECK_AND_SAVE_GL_ERROR(); |
168 } else { | 351 } else { |
169 // Should match the clear color of RenderWidgetHostViewMac. | 352 // Should match the clear color of RenderWidgetHostViewMac. |
170 glClearColor(1.0f, 1.0f, 1.0f, 1.0f); | 353 glClearColor(1.0f, 1.0f, 1.0f, 1.0f); |
171 glClear(GL_COLOR_BUFFER_BIT); | 354 glClear(GL_COLOR_BUFFER_BIT); |
172 } | 355 } |
173 | 356 |
174 bool workaround_needed = | 357 bool workaround_needed = |
175 GpuDataManagerImpl::GetInstance()->IsDriverBugWorkaroundActive( | 358 GpuDataManagerImpl::GetInstance()->IsDriverBugWorkaroundActive( |
176 gpu::FORCE_GL_FINISH_AFTER_COMPOSITING); | 359 gpu::FORCE_GL_FINISH_AFTER_COMPOSITING); |
177 if (workaround_needed) { | 360 if (workaround_needed) { |
(...skipping 12 matching lines...) Expand all Loading... |
190 // generate an error. Clear the GL error afterwards just in case. | 373 // generate an error. Clear the GL error afterwards just in case. |
191 glClearColor(0.8, 0.8, 0.8, 1.0); | 374 glClearColor(0.8, 0.8, 0.8, 1.0); |
192 glClear(GL_COLOR_BUFFER_BIT); | 375 glClear(GL_COLOR_BUFFER_BIT); |
193 glGetError(); | 376 glGetError(); |
194 } | 377 } |
195 | 378 |
196 eviction_has_been_drawn_since_updated_ = true; | 379 eviction_has_been_drawn_since_updated_ = true; |
197 return result; | 380 return result; |
198 } | 381 } |
199 | 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 |
200 bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent( | 447 bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent( |
201 const scoped_refptr<CompositingIOSurfaceContext>& current_context, | 448 const scoped_refptr<CompositingIOSurfaceContext>& current_context, |
202 const gfx::Size pixel_size, | 449 const gfx::Size pixel_size, |
203 float scale_factor, | 450 float scale_factor, |
204 IOSurfaceID io_surface_handle) { | 451 IOSurfaceID io_surface_handle) { |
205 TRACE_EVENT0("browser", "CompositingIOSurfaceMac::MapIOSurfaceToTexture"); | 452 TRACE_EVENT0("browser", "CompositingIOSurfaceMac::MapIOSurfaceToTexture"); |
206 | 453 |
207 if (!io_surface_ || io_surface_handle != io_surface_handle_) | 454 if (!io_surface_ || io_surface_handle != io_surface_handle_) |
208 UnrefIOSurfaceWithContextCurrent(); | 455 UnrefIOSurfaceWithContextCurrent(); |
209 | 456 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
295 io_surface_.reset(); | 542 io_surface_.reset(); |
296 | 543 |
297 // Forget the ID, because even if it is still around when we want to use it | 544 // Forget the ID, because even if it is still around when we want to use it |
298 // again, OSX may have reused the same ID for a new tab and we don't want to | 545 // again, OSX may have reused the same ID for a new tab and we don't want to |
299 // blit random tab contents. | 546 // blit random tab contents. |
300 io_surface_handle_ = 0; | 547 io_surface_handle_ = 0; |
301 | 548 |
302 EvictionMarkEvicted(); | 549 EvictionMarkEvicted(); |
303 } | 550 } |
304 | 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 |
305 bool CompositingIOSurfaceMac::HasBeenPoisoned() const { | 562 bool CompositingIOSurfaceMac::HasBeenPoisoned() const { |
306 return offscreen_context_->HasBeenPoisoned(); | 563 return offscreen_context_->HasBeenPoisoned(); |
307 } | 564 } |
308 | 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 ©_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 ©_context->output_textures[0], |
| 627 ©_context->output_textures[1], |
| 628 ©_context->output_textures[2], |
| 629 ©_context->output_texture_sizes[0], |
| 630 ©_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 |
309 GLenum CompositingIOSurfaceMac::GetAndSaveGLError() { | 894 GLenum CompositingIOSurfaceMac::GetAndSaveGLError() { |
310 GLenum gl_error = glGetError(); | 895 GLenum gl_error = glGetError(); |
311 if (gl_error_ == GL_NO_ERROR) | 896 if (gl_error_ == GL_NO_ERROR) |
312 gl_error_ = gl_error; | 897 gl_error_ = gl_error; |
313 return gl_error; | 898 return gl_error; |
314 } | 899 } |
315 | 900 |
316 void CompositingIOSurfaceMac::EvictionMarkUpdated() { | 901 void CompositingIOSurfaceMac::EvictionMarkUpdated() { |
317 EvictionMarkEvicted(); | 902 EvictionMarkEvicted(); |
318 eviction_queue_.Get().push_back(this); | 903 eviction_queue_.Get().push_back(this); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
354 | 939 |
355 // If the number of IOSurfaces allocated is less than the threshold, | 940 // If the number of IOSurfaces allocated is less than the threshold, |
356 // stop walking the list of surfaces. | 941 // stop walking the list of surfaces. |
357 if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces) | 942 if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces) |
358 break; | 943 break; |
359 | 944 |
360 // Don't evict anything that has not yet been drawn. | 945 // Don't evict anything that has not yet been drawn. |
361 if (!surface->eviction_has_been_drawn_since_updated_) | 946 if (!surface->eviction_has_been_drawn_since_updated_) |
362 continue; | 947 continue; |
363 | 948 |
| 949 // Don't evict anything with pending copy requests. |
| 950 if (!surface->copy_requests_.empty()) |
| 951 continue; |
| 952 |
364 // Evict the surface. | 953 // Evict the surface. |
365 surface->UnrefIOSurface(); | 954 surface->UnrefIOSurface(); |
366 } | 955 } |
367 } | 956 } |
368 | 957 |
369 // static | 958 // static |
370 base::LazyInstance<CompositingIOSurfaceMac::EvictionQueue> | 959 base::LazyInstance<CompositingIOSurfaceMac::EvictionQueue> |
371 CompositingIOSurfaceMac::eviction_queue_; | 960 CompositingIOSurfaceMac::eviction_queue_; |
372 | 961 |
373 // static | 962 // static |
374 bool CompositingIOSurfaceMac::eviction_scheduled_ = false; | 963 bool CompositingIOSurfaceMac::eviction_scheduled_ = false; |
375 | 964 |
376 } // namespace content | 965 } // namespace content |
OLD | NEW |