Chromium Code Reviews| Index: cc/output/gl_renderer.cc |
| diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc |
| index 4a0c7ba1fcf3d132ce09bcb04e7b269e748b19bc..54016b47d8363442a1fe655d73173a415dde9d1d 100644 |
| --- a/cc/output/gl_renderer.cc |
| +++ b/cc/output/gl_renderer.cc |
| @@ -30,6 +30,7 @@ |
| #include "cc/resources/layer_quad.h" |
| #include "cc/resources/priority_calculator.h" |
| #include "cc/resources/scoped_resource.h" |
| +#include "cc/resources/sync_point_helper.h" |
| #include "cc/trees/damage_tracker.h" |
| #include "cc/trees/proxy.h" |
| #include "cc/trees/single_thread_proxy.h" |
| @@ -87,6 +88,17 @@ const float kAntiAliasingEpsilon = 1.0f / 1024.0f; |
| } // anonymous namespace |
| +struct GLRenderer::PendingAsyncReadPixels { |
| + PendingAsyncReadPixels() : sync_point(0) {} |
| + |
| + CopyRenderPassCallback copy_callback; |
| + base::CancelableClosure finished_read_pixels_callback; |
| + unsigned sync_point; |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(PendingAsyncReadPixels); |
| +}; |
| + |
| scoped_ptr<GLRenderer> GLRenderer::Create(RendererClient* client, |
| OutputSurface* output_surface, |
| ResourceProvider* resource_provider, |
| @@ -192,6 +204,13 @@ bool GLRenderer::Initialize() { |
| } |
| GLRenderer::~GLRenderer() { |
| + while (!pending_async_read_pixels_.empty()) { |
| + pending_async_read_pixels_.back()->finished_read_pixels_callback.Cancel(); |
| + pending_async_read_pixels_.back()->copy_callback.Run( |
| + scoped_ptr<SkBitmap>()); |
| + pending_async_read_pixels_.pop_back(); |
| + } |
| + |
| context_->setMemoryAllocationChangedCallbackCHROMIUM(NULL); |
| CleanupSharedObjects(); |
| } |
| @@ -1776,17 +1795,10 @@ void GLRenderer::EnsureScissorTestDisabled() { |
| is_scissor_enabled_ = false; |
| } |
| -void GLRenderer::CopyCurrentRenderPassToBitmap(DrawingFrame* frame, |
| - SkBitmap* bitmap) { |
| - gfx::Size render_pass_size = frame->current_render_pass->output_rect.size(); |
| - bitmap->setConfig(SkBitmap::kARGB_8888_Config, |
| - render_pass_size.width(), |
| - render_pass_size.height()); |
| - if (bitmap->allocPixels()) { |
| - bitmap->lockPixels(); |
| - GetFramebufferPixels(bitmap->getPixels(), gfx::Rect(render_pass_size)); |
| - bitmap->unlockPixels(); |
| - } |
| +void GLRenderer::CopyCurrentRenderPassToBitmap( |
| + DrawingFrame* frame, |
| + const CopyRenderPassCallback& callback) { |
| + GetFramebufferPixels(frame->current_render_pass->output_rect, callback); |
| } |
| void GLRenderer::ToGLMatrix(float* gl_matrix, const gfx::Transform& transform) { |
| @@ -1988,18 +2000,68 @@ void GLRenderer::EnsureBackbuffer() { |
| } |
| void GLRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { |
| + if (!pixels || rect.IsEmpty()) |
| + return; |
| + |
| + scoped_ptr<PendingAsyncReadPixels> pending_read(new PendingAsyncReadPixels); |
| + pending_async_read_pixels_.insert(pending_async_read_pixels_.begin(), |
| + pending_read.Pass()); |
| + |
| + // This is a syncronous call since the callback is null. |
| + DoGetFramebufferPixels(static_cast<uint8*>(pixels), |
| + rect, |
| + AsyncGetFramebufferPixelsCleanupCallback()); |
| +} |
| + |
| +void GLRenderer::GetFramebufferPixels(gfx::Rect rect, |
| + CopyRenderPassCallback callback) { |
| + if (callback.is_null()) |
| + return; |
| + if (rect.IsEmpty()) { |
| + callback.Run(scoped_ptr<SkBitmap>()); |
| + return; |
| + } |
| + |
| + scoped_ptr<SkBitmap> bitmap(new SkBitmap); |
| + bitmap->setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height()); |
| + bitmap->allocPixels(); |
| + |
| + scoped_ptr<SkAutoLockPixels> lock(new SkAutoLockPixels(*bitmap)); |
| + |
| + // Save a pointer to the pixels, the bitmap is owned by the cleanup_callback. |
| + uint8* pixels = static_cast<uint8*>(bitmap->getPixels()); |
| + |
| + AsyncGetFramebufferPixelsCleanupCallback cleanup_callback = base::Bind( |
| + &GLRenderer::PassOnSkBitmap, |
| + base::Unretained(this), |
| + base::Passed(&bitmap), |
| + base::Passed(&lock), |
| + callback); |
| + |
| + scoped_ptr<PendingAsyncReadPixels> pending_read(new PendingAsyncReadPixels); |
| + pending_read->copy_callback = callback; |
| + pending_async_read_pixels_.insert(pending_async_read_pixels_.begin(), |
| + pending_read.Pass()); |
| + |
| + // This is an asyncronous call since the callback is not null. |
| + DoGetFramebufferPixels(pixels, rect, cleanup_callback); |
| +} |
| + |
| +void GLRenderer::DoGetFramebufferPixels( |
| + uint8* dest_pixels, |
| + gfx::Rect rect, |
| + const AsyncGetFramebufferPixelsCleanupCallback& cleanup_callback) { |
| DCHECK(rect.right() <= ViewportWidth()); |
| DCHECK(rect.bottom() <= ViewportHeight()); |
| - if (!pixels) |
| - return; |
| + bool is_async = !cleanup_callback.is_null(); |
| MakeContextCurrent(); |
| bool do_workaround = NeedsIOSurfaceReadbackWorkaround(); |
| - GLuint temporary_texture = 0; |
| - GLuint temporary_fbo = 0; |
| + unsigned temporary_texture = 0; |
| + unsigned temporary_fbo = 0; |
| if (do_workaround) { |
| // On Mac OS X, calling glReadPixels() against an FBO whose color attachment |
| @@ -2047,8 +2109,21 @@ void GLRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { |
| GL_FRAMEBUFFER_COMPLETE); |
| } |
| - scoped_ptr<uint8_t[]> src_pixels( |
| - new uint8_t[rect.width() * rect.height() * 4]); |
| + unsigned buffer = 0; |
| + scoped_ptr<uint8[]> sync_pixels; |
| + |
| + if (is_async) { |
|
piman
2013/04/27 00:42:25
I think you can make your life a bit easier if:
-
danakj
2013/04/29 14:59:05
Thanks! This is what I was trying to do at first,
|
| + buffer = context_->createBuffer(); |
| + GLC(context_, context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, |
| + buffer)); |
| + GLC(context_, context_->bufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, |
| + 4 * rect.size().GetArea(), |
| + NULL, |
| + GL_STREAM_READ)); |
| + } else { |
| + sync_pixels.reset(new uint8[4 * rect.size().GetArea()]); |
| + } |
| + |
| GLC(context_, |
| context_->readPixels(rect.x(), |
| ViewportSize().height() - rect.bottom(), |
| @@ -2056,22 +2131,11 @@ void GLRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { |
| rect.height(), |
| GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| - src_pixels.get())); |
| - |
| - uint8_t* dest_pixels = static_cast<uint8_t*>(pixels); |
| - size_t row_bytes = rect.width() * 4; |
| - int num_rows = rect.height(); |
| - size_t total_bytes = num_rows * row_bytes; |
| - for (size_t dest_y = 0; dest_y < total_bytes; dest_y += row_bytes) { |
| - // Flip Y axis. |
| - size_t src_y = total_bytes - dest_y - row_bytes; |
| - // Swizzle BGRA -> RGBA. |
| - for (size_t x = 0; x < row_bytes; x += 4) { |
| - dest_pixels[dest_y + (x + 0)] = src_pixels.get()[src_y + (x + 2)]; |
| - dest_pixels[dest_y + (x + 1)] = src_pixels.get()[src_y + (x + 1)]; |
| - dest_pixels[dest_y + (x + 2)] = src_pixels.get()[src_y + (x + 0)]; |
| - dest_pixels[dest_y + (x + 3)] = src_pixels.get()[src_y + (x + 3)]; |
| - } |
| + sync_pixels.get())); |
| + |
| + if (is_async) { |
| + GLC(context_, context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, |
| + 0)); |
| } |
| if (do_workaround) { |
| @@ -2082,9 +2146,109 @@ void GLRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { |
| GLC(context_, context_->deleteTexture(temporary_texture)); |
| } |
| + unsigned sync_point = 0; |
| + if (is_async) |
| + sync_point = context_->insertSyncPoint(); |
| + pending_async_read_pixels_.back()->sync_point = sync_point; |
| + |
| + base::Closure finished_callback = |
| + base::Bind(&GLRenderer::FinishedReadback, |
| + base::Unretained(this), |
| + cleanup_callback, |
| + buffer, |
| + base::Passed(&sync_pixels), |
| + dest_pixels, |
| + rect.size(), |
| + sync_point); |
|
piman
2013/04/27 00:42:25
nit: Given the above, and given that you only pass
danakj
2013/04/29 14:59:05
Oh, good idea.
|
| + // Save the finished_callback so it can be cancelled. |
| + pending_async_read_pixels_.back()->finished_read_pixels_callback.Reset( |
| + finished_callback); |
| + |
| + if (is_async) { |
| + SyncPointHelper::SignalSyncPoint( |
| + context_, |
| + sync_point, |
| + finished_callback); |
| + } else { |
| + finished_callback.Run(); |
| + } |
| + |
| EnforceMemoryPolicy(); |
| } |
| +void GLRenderer::FinishedReadback( |
| + const AsyncGetFramebufferPixelsCleanupCallback& cleanup_callback, |
| + unsigned source_buffer, |
| + scoped_ptr<uint8[]> sync_pixels, |
| + uint8* dest_pixels, |
| + gfx::Size size, |
| + unsigned sync_point) { |
| + DCHECK(!pending_async_read_pixels_.empty()); |
| + DCHECK_EQ(sync_point, pending_async_read_pixels_.back()->sync_point); |
| + |
| + uint8* src_pixels = NULL; |
| + bool success = false; |
| + |
| + if (sync_pixels) { |
| + src_pixels = sync_pixels.get(); |
| + success = true; |
| + } else if (source_buffer != 0) { |
| + GLC(context_, context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, |
| + source_buffer)); |
| + src_pixels = static_cast<uint8*>( |
| + context_->mapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, |
| + GL_READ_ONLY)); |
| + |
| + if (src_pixels) { |
| + memcpy(dest_pixels, src_pixels, 4 * size.GetArea()); |
| + GLC(context_, context_->unmapBufferCHROMIUM( |
| + GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM)); |
| + success = true; |
| + } |
| + GLC(context_, context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, |
| + 0)); |
| + GLC(context_, context_->deleteBuffer(source_buffer)); |
| + } |
| + |
| + if (src_pixels) { |
| + size_t row_bytes = size.width() * 4; |
| + int num_rows = size.height(); |
| + size_t total_bytes = num_rows * row_bytes; |
| + for (size_t dest_y = 0; dest_y < total_bytes; dest_y += row_bytes) { |
| + // Flip Y axis. |
| + size_t src_y = total_bytes - dest_y - row_bytes; |
| + // Swizzle BGRA -> RGBA. |
| + for (size_t x = 0; x < row_bytes; x += 4) { |
| + dest_pixels[dest_y + (x + 0)] = src_pixels[src_y + (x + 2)]; |
| + dest_pixels[dest_y + (x + 1)] = src_pixels[src_y + (x + 1)]; |
| + dest_pixels[dest_y + (x + 2)] = src_pixels[src_y + (x + 0)]; |
| + dest_pixels[dest_y + (x + 3)] = src_pixels[src_y + (x + 3)]; |
| + } |
| + } |
| + } |
| + |
| + // TODO(danakj): This can go away when synchronous readback is no more and its |
| + // contents can just move here. |
| + if (!cleanup_callback.is_null()) |
| + cleanup_callback.Run(success); |
| + |
| + pending_async_read_pixels_.pop_back(); |
| +} |
| + |
| +void GLRenderer::PassOnSkBitmap( |
| + scoped_ptr<SkBitmap> bitmap, |
| + scoped_ptr<SkAutoLockPixels> lock, |
| + const CopyRenderPassCallback& callback, |
| + bool success) { |
| + DCHECK(callback.Equals(pending_async_read_pixels_.back()->copy_callback)); |
| + |
| + lock.reset(); |
| + if (success) |
| + callback.Run(bitmap.Pass()); |
| + else |
| + callback.Run(scoped_ptr<SkBitmap>()); |
| +} |
| + |
| bool GLRenderer::GetFramebufferTexture(ScopedResource* texture, |
| gfx::Rect device_rect) { |
| DCHECK(!texture->id() || (texture->size() == device_rect.size() && |