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..2841f873ed512a2206a4af3847f80de0f70ab874 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" |
| @@ -119,6 +120,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(); |
| @@ -221,7 +229,8 @@ CompositingIOSurfaceMac::CompositingIOSurfaceMac( |
| GLuint shader_program_white, |
| bool is_vsync_disabled, |
| CVDisplayLinkRef display_link) |
| - : io_surface_support_(io_surface_support), |
| + : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
| + io_surface_support_(io_surface_support), |
| glContext_(glContext), |
| cglContext_(cglContext), |
| io_surface_handle_(0), |
| @@ -269,7 +278,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, |
| @@ -282,8 +294,9 @@ void CompositingIOSurfaceMac::SetIOSurface(uint64 io_surface_handle, |
| void CompositingIOSurfaceMac::DrawIOSurface(NSView* view, float scale_factor) { |
| CGLSetCurrentContext(cglContext_); |
| - |
| bool has_io_surface = MapIOSurfaceToTexture(io_surface_handle_); |
| + if (!has_io_surface) |
| + return; |
| TRACE_EVENT1("browser", "CompositingIOSurfaceMac::DrawIOSurface", |
| "has_io_surface", has_io_surface); |
| @@ -385,79 +398,111 @@ 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; |
| + TRACE_EVENT0("browser", "CompositingIOSurfaceMac::CopyTo()"); |
| - GLuint dst_texture = 0; |
| - glGenTextures(1, &dst_texture); CHECK_GL_ERROR(); |
| - glBindTexture(target, dst_texture); CHECK_GL_ERROR(); |
| + CGLSetCurrentContext(cglContext_); |
| + if (!MapIOSurfaceToTexture(io_surface_handle_)) { |
| + CGLSetCurrentContext(0); |
| + callback.Run(false); |
| + return; |
| + } |
| - GLuint dst_framebuffer = 0; |
| - glGenFramebuffersEXT(1, &dst_framebuffer); CHECK_GL_ERROR(); |
| - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dst_framebuffer); 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; |
| + |
| + // Create an offscreen framebuffer. |
| + // This is used to render and scale a subrect of IOSurface. |
| + const GLenum kTarget = GL_TEXTURE_RECTANGLE_ARB; |
| + const int kDestWidth = copy_context_.dest_size.width(); |
| + const int kDestHeight = copy_context_.dest_size.height(); |
| + |
| + 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(target, |
| + glTexImage2D(kTarget, |
| 0, |
| GL_RGBA, |
| - dst_pixel_size.width(), |
| - dst_pixel_size.height(), |
| + kDestWidth, |
| + kDestHeight, |
| 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()); |
| + glViewport(0, 0, kDestWidth, kDestHeight); CHECK_GL_ERROR(); |
| + glMatrixMode(GL_PROJECTION); CHECK_GL_ERROR(); |
| + glLoadIdentity(); CHECK_GL_ERROR(); |
| + glOrtho(0, kDestWidth, 0, kDestHeight, -1, 1); CHECK_GL_ERROR(); |
| + glMatrixMode(GL_MODELVIEW); CHECK_GL_ERROR(); |
| + glLoadIdentity(); CHECK_GL_ERROR(); |
| - glMatrixMode(GL_PROJECTION); |
| - glLoadIdentity(); |
| - glOrtho(0, dst_pixel_size.width(), 0, dst_pixel_size.height(), -1, 1); |
| - glMatrixMode(GL_MODELVIEW); |
| - glLoadIdentity(); |
| + glDisable(GL_DEPTH_TEST); CHECK_GL_ERROR(); |
| + glDisable(GL_BLEND); CHECK_GL_ERROR(); |
| - glDisable(GL_DEPTH_TEST); |
| - glDisable(GL_BLEND); |
| + glUseProgram(shader_program_blit_rgb_); CHECK_GL_ERROR(); |
| - glUseProgram(shader_program_blit_rgb_); |
| - |
| - 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, kDestWidth, kDestHeight); 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); |
| + glBindTexture(kTarget, 0); CHECK_GL_ERROR(); |
| + glUseProgram(0); CHECK_GL_ERROR(); |
| - CGLFlushDrawable(cglContext_); |
| - |
| - 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, |
| + kDestWidth * kDestHeight * 4, |
| + NULL, GL_STREAM_READ_ARB); CHECK_GL_ERROR(); |
| + glReadPixels(0, 0, kDestWidth, kDestHeight, 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(); |
| + CGLSetCurrentContext(0); |
| - glDeleteFramebuffersEXT(1, &dst_framebuffer); |
| - glDeleteTextures(1, &dst_texture); |
| + // 30ms is an estimate assuming most hardware can complete asynchronous |
| + // readback within this time limit. |
| + const int kDelayMilliseconds = 30; |
|
Ken Russell (switch to Gerrit)
2012/09/18 07:57:42
Please figure out some way to avoid having a heuri
|
| - CGLSetCurrentContext(0); |
| - return true; |
| + MessageLoop::current()->PostDelayedTask( |
| + FROM_HERE, |
| + base::Bind(&CompositingIOSurfaceMac::FinishCopy, |
| + weak_factory_.GetWeakPtr()), |
| + base::TimeDelta::FromMilliseconds(kDelayMilliseconds) |
| + ); |
| } |
| bool CompositingIOSurfaceMac::MapIOSurfaceToTexture( |
| @@ -617,4 +662,42 @@ void CompositingIOSurfaceMac::StopDisplayLink() { |
| CVDisplayLinkStop(display_link_); |
| } |
| +void CompositingIOSurfaceMac::FinishCopy() { |
| + CHECK(copy_context_.started); |
| + TRACE_EVENT0("browser", "CompositingIOSurfaceMac::FinishCopy()"); |
| + |
| + CGLSetCurrentContext(cglContext_); |
| + |
| + const int kDestWidth = copy_context_.dest_size.width(); |
| + const int kDestHeight = copy_context_.dest_size.height(); |
| + |
| + 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, kDestWidth * kDestHeight * 4); |
| + glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); CHECK_GL_ERROR(); |
| + } |
| + glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); CHECK_GL_ERROR(); |
| + CGLSetCurrentContext(0); |
| + |
| + base::Callback<void(bool)> callback = copy_context_.callback; |
| + CleanupResourcesForCopy(); |
| + |
| + 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(); |
| + copy_context_.Reset(); |
| +} |
| + |
| } // namespace content |