Chromium Code Reviews| Index: content/browser/renderer_host/compositing_iosurface_mac.mm |
| diff --git a/content/browser/renderer_host/compositing_iosurface_mac.mm b/content/browser/renderer_host/compositing_iosurface_mac.mm |
| index bb6050fb09166736ef99dbcb9396ece94196075d..5d118a106fa24abfc1e182ea1edf28603b381835 100644 |
| --- a/content/browser/renderer_host/compositing_iosurface_mac.mm |
| +++ b/content/browser/renderer_host/compositing_iosurface_mac.mm |
| @@ -9,6 +9,7 @@ |
| #include "base/command_line.h" |
| #include "base/debug/trace_event.h" |
| +#include "base/message_loop.h" |
| #include "base/threading/platform_thread.h" |
| #include "content/common/content_constants_internal.h" |
| #include "content/public/browser/browser_thread.h" |
| @@ -105,6 +106,19 @@ GLuint CreateProgramGLSL(const char* vertex_shader_str, |
| return program; |
| } |
| +bool HasAppleFenceExtension() { |
| + static bool initialized_has_fence = false; |
| + static bool has_fence = false; |
| + |
| + if (!initialized_has_fence) { |
| + has_fence = |
| + strstr(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)), |
| + "GL_APPLE_fence") != NULL; |
|
Nico
2012/09/22 11:12:21
do we not have a real "HasExtension()" function an
|
| + initialized_has_fence = true; |
| + } |
| + return has_fence; |
| +} |
| + |
| } // namespace |
| CVReturn DisplayLinkCallback(CVDisplayLinkRef display_link, |
| @@ -119,6 +133,13 @@ CVReturn DisplayLinkCallback(CVDisplayLinkRef display_link, |
| return kCVReturnSuccess; |
| } |
| +CompositingIOSurfaceMac::CopyContext::CopyContext() { |
| + Reset(); |
| +} |
| + |
| +CompositingIOSurfaceMac::CopyContext::~CopyContext() { |
| +} |
| + |
| CompositingIOSurfaceMac* CompositingIOSurfaceMac::Create() { |
| TRACE_EVENT0("browser", "CompositingIOSurfaceMac::Create"); |
| IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); |
| @@ -269,7 +290,10 @@ void CompositingIOSurfaceMac::GetVSyncParameters(base::TimeTicks* timebase, |
| CompositingIOSurfaceMac::~CompositingIOSurfaceMac() { |
| CVDisplayLinkRelease(display_link_); |
| - UnrefIOSurface(); |
| + CGLSetCurrentContext(cglContext_); |
| + CleanupResourcesForCopy(); |
| + UnrefIOSurfaceWithContextCurrent(); |
| + CGLSetCurrentContext(0); |
| } |
| void CompositingIOSurfaceMac::SetIOSurface(uint64 io_surface_handle, |
| @@ -385,79 +409,120 @@ void CompositingIOSurfaceMac::DrawIOSurface(NSView* view, float scale_factor) { |
| RateLimitDraws(); |
| } |
| -bool CompositingIOSurfaceMac::CopyTo( |
| +void CompositingIOSurfaceMac::CopyTo( |
| const gfx::Rect& src_pixel_subrect, |
| const gfx::Size& dst_pixel_size, |
| - void* out) { |
| - if (!MapIOSurfaceToTexture(io_surface_handle_)) |
| - return false; |
| + void* out, |
| + const base::Callback<void(bool)>& callback) { |
| + if (copy_context_.started) { |
| + callback.Run(false); |
| + return; |
| + } |
| CGLSetCurrentContext(cglContext_); |
| - GLuint target = GL_TEXTURE_RECTANGLE_ARB; |
| + if (!MapIOSurfaceToTexture(io_surface_handle_)) { |
| + CGLSetCurrentContext(0); |
| + callback.Run(false); |
| + return; |
| + } |
| + |
| + TRACE_EVENT0("browser", "CompositingIOSurfaceMac::CopyTo()"); |
| - GLuint dst_texture = 0; |
| - glGenTextures(1, &dst_texture); CHECK_GL_ERROR(); |
| - glBindTexture(target, dst_texture); CHECK_GL_ERROR(); |
| + copy_context_.started = true; |
| + copy_context_.src_rect = src_pixel_subrect; |
| + copy_context_.dest_size = dst_pixel_size; |
| + copy_context_.out_buf = out; |
| + copy_context_.callback = callback; |
| + |
| + const bool use_fence = HasAppleFenceExtension(); |
| + if (use_fence) { |
| + glGenFencesAPPLE(1, ©_context_.fence); CHECK_GL_ERROR(); |
| + copy_context_.use_fence = true; |
| + copy_context_.cycles_elapsed = 0; |
| + } |
| - GLuint dst_framebuffer = 0; |
| - glGenFramebuffersEXT(1, &dst_framebuffer); CHECK_GL_ERROR(); |
| - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dst_framebuffer); CHECK_GL_ERROR(); |
| + // Create an offscreen framebuffer. |
| + // This is used to render and scale a subrect of IOSurface. |
| + const GLenum kTarget = GL_TEXTURE_RECTANGLE_ARB; |
| + const int dest_width = copy_context_.dest_size.width(); |
| + const int dest_height = copy_context_.dest_size.height(); |
| - glTexImage2D(target, |
| + glGenTextures(1, ©_context_.frame_buffer_texture); CHECK_GL_ERROR(); |
| + glBindTexture(kTarget, copy_context_.frame_buffer_texture); CHECK_GL_ERROR(); |
| + glGenFramebuffersEXT(1, ©_context_.frame_buffer); CHECK_GL_ERROR(); |
| + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, copy_context_.frame_buffer); |
| + CHECK_GL_ERROR(); |
| + |
| + glTexImage2D(kTarget, |
| 0, |
| GL_RGBA, |
| - dst_pixel_size.width(), |
| - dst_pixel_size.height(), |
| + dest_width, |
| + dest_height, |
| 0, |
| GL_BGRA, |
| GL_UNSIGNED_INT_8_8_8_8_REV, |
| NULL); CHECK_GL_ERROR(); |
| glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, |
| GL_COLOR_ATTACHMENT0_EXT, |
| - target, |
| - dst_texture, |
| + kTarget, |
| + copy_context_.frame_buffer_texture, |
| 0); CHECK_GL_ERROR(); |
| - glBindTexture(target, 0); CHECK_GL_ERROR(); |
| - |
| - glViewport(0, 0, dst_pixel_size.width(), dst_pixel_size.height()); |
| - glMatrixMode(GL_PROJECTION); |
| - glLoadIdentity(); |
| - glOrtho(0, dst_pixel_size.width(), 0, dst_pixel_size.height(), -1, 1); |
| - glMatrixMode(GL_MODELVIEW); |
| - glLoadIdentity(); |
| + glViewport(0, 0, dest_width, dest_height); CHECK_GL_ERROR(); |
| + glMatrixMode(GL_PROJECTION); CHECK_GL_ERROR(); |
| + glLoadIdentity(); CHECK_GL_ERROR(); |
| + glOrtho(0, dest_width, 0, dest_height, -1, 1); CHECK_GL_ERROR(); |
| + glMatrixMode(GL_MODELVIEW); CHECK_GL_ERROR(); |
| + glLoadIdentity(); CHECK_GL_ERROR(); |
| - glDisable(GL_DEPTH_TEST); |
| - glDisable(GL_BLEND); |
| + glDisable(GL_DEPTH_TEST); CHECK_GL_ERROR(); |
| + glDisable(GL_BLEND); CHECK_GL_ERROR(); |
| - glUseProgram(shader_program_blit_rgb_); |
| + glUseProgram(shader_program_blit_rgb_); CHECK_GL_ERROR(); |
| - int texture_unit = 0; |
| - glUniform1i(blit_rgb_sampler_location_, texture_unit); |
| - glActiveTexture(GL_TEXTURE0 + texture_unit); |
| - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_); |
| + const int kTextureUnit = 0; |
| + glUniform1i(blit_rgb_sampler_location_, kTextureUnit); CHECK_GL_ERROR(); |
| + glActiveTexture(GL_TEXTURE0 + kTextureUnit); CHECK_GL_ERROR(); |
| + glBindTexture(kTarget, texture_); CHECK_GL_ERROR(); |
| + glTexParameterf(kTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); CHECK_GL_ERROR(); |
| + glTexParameterf(kTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); CHECK_GL_ERROR(); |
| SurfaceQuad quad; |
| - quad.set_rect(0.0f, 0.0f, dst_pixel_size.width(), dst_pixel_size.height()); |
| - quad.set_texcoord_rect(src_pixel_subrect.x(), src_pixel_subrect.y(), |
| - src_pixel_subrect.right(), src_pixel_subrect.bottom()); |
| + quad.set_rect(0.0f, 0.0f, dest_width, dest_height); CHECK_GL_ERROR(); |
| + quad.set_texcoord_rect( |
| + copy_context_.src_rect.x(), copy_context_.src_rect.y(), |
| + copy_context_.src_rect.right(), copy_context_.src_rect.bottom()); |
| DrawQuad(quad); |
| - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); CHECK_GL_ERROR(); |
| - glUseProgram(0); |
| - |
| - CGLFlushDrawable(cglContext_); |
| + glBindTexture(kTarget, 0); CHECK_GL_ERROR(); |
| + glUseProgram(0); CHECK_GL_ERROR(); |
| - glReadPixels(0, 0, dst_pixel_size.width(), dst_pixel_size.height(), |
| - GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, out); |
| + // Copy the offscreen framebuffer to a PBO. |
| + glGenBuffersARB(1, ©_context_.pixel_buffer); CHECK_GL_ERROR(); |
| + glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, copy_context_.pixel_buffer); |
| + CHECK_GL_ERROR(); |
| + glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, |
| + dest_width * dest_height * 4, |
| + NULL, GL_STREAM_READ_ARB); CHECK_GL_ERROR(); |
| + glReadPixels(0, 0, dest_width, dest_height, GL_BGRA, |
| + GL_UNSIGNED_INT_8_8_8_8_REV, 0); CHECK_GL_ERROR(); |
| + glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); CHECK_GL_ERROR(); |
| glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); CHECK_GL_ERROR(); |
| - glDeleteFramebuffersEXT(1, &dst_framebuffer); |
| - glDeleteTextures(1, &dst_texture); |
| - |
| + if (use_fence) { |
| + glSetFenceAPPLE(copy_context_.fence); CHECK_GL_ERROR(); |
| + } |
| + glFlush(); CHECK_GL_ERROR(); |
| CGLSetCurrentContext(0); |
| - return true; |
| + |
| + // 20ms is an estimate assuming most hardware can complete asynchronous |
| + // readback within this time limit. The timer will keep running until |
| + // operation is completed. |
| + const int kIntervalMilliseconds = 20; |
| + copy_timer_.Start(FROM_HERE, |
| + base::TimeDelta::FromMilliseconds(kIntervalMilliseconds), |
| + this, &CompositingIOSurfaceMac::FinishCopy); |
| } |
| bool CompositingIOSurfaceMac::MapIOSurfaceToTexture( |
| @@ -617,4 +682,57 @@ void CompositingIOSurfaceMac::StopDisplayLink() { |
| CVDisplayLinkStop(display_link_); |
| } |
| +void CompositingIOSurfaceMac::FinishCopy() { |
| + CHECK(copy_context_.started); |
| + TRACE_EVENT0("browser", "CompositingIOSurfaceMac::FinishCopy()"); |
| + |
| + CGLSetCurrentContext(cglContext_); |
| + |
| + if (copy_context_.use_fence) { |
| + bool copy_completed = glTestFenceAPPLE(copy_context_.fence); |
| + CHECK_GL_ERROR(); |
| + |
| + // Allow 1s for the operation to complete. |
| + const int kRetryCycles = 50; |
| + |
| + if (!copy_completed && copy_context_.cycles_elapsed < kRetryCycles) { |
| + ++copy_context_.cycles_elapsed; |
| + CGLSetCurrentContext(0); |
| + return; |
| + } |
| + } |
| + copy_timer_.Stop(); |
| + |
| + glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, copy_context_.pixel_buffer); |
| + CHECK_GL_ERROR(); |
| + |
| + void* buf = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); |
| + CHECK_GL_ERROR(); |
| + |
| + if (buf) { |
| + memcpy(copy_context_.out_buf, buf, copy_context_.dest_size.GetArea() * 4); |
| + glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); CHECK_GL_ERROR(); |
| + } |
| + glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); CHECK_GL_ERROR(); |
| + |
| + base::Callback<void(bool)> callback = copy_context_.callback; |
| + CleanupResourcesForCopy(); |
| + CGLSetCurrentContext(0); |
| + |
| + callback.Run(buf != NULL); |
| +} |
| + |
| +void CompositingIOSurfaceMac::CleanupResourcesForCopy() { |
| + if (!copy_context_.started) |
| + return; |
| + |
| + glDeleteFramebuffersEXT(1, ©_context_.frame_buffer); CHECK_GL_ERROR(); |
| + glDeleteTextures(1, ©_context_.frame_buffer_texture); CHECK_GL_ERROR(); |
| + glDeleteBuffers(1, ©_context_.pixel_buffer); CHECK_GL_ERROR(); |
| + if (copy_context_.use_fence) { |
| + glDeleteFencesAPPLE(1, ©_context_.fence); CHECK_GL_ERROR(); |
| + } |
| + copy_context_.Reset(); |
| +} |
| + |
| } // namespace content |