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