Index: cc/output/gl_renderer.cc |
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc |
index adfcf473665d0b7e0e317910b8d247a6258906f5..38ce6a6d1ca5343c5d18c6ac0b73e4d55402af7e 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() : buffer(0) {} |
+ |
+ CopyRenderPassCallback copy_callback; |
+ base::CancelableClosure finished_read_pixels_callback; |
+ unsigned buffer; |
+ |
+ 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(); |
} |
@@ -1777,17 +1796,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) { |
+ GetFramebufferPixelsAsync(frame->current_render_pass->output_rect, callback); |
} |
void GLRenderer::ToGLMatrix(float* gl_matrix, const gfx::Transform& transform) { |
@@ -1989,18 +2001,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::GetFramebufferPixelsAsync(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 |
@@ -2048,8 +2110,14 @@ 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 = 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)); |
+ |
GLC(context_, |
context_->readPixels(rect.x(), |
ViewportSize().height() - rect.bottom(), |
@@ -2057,23 +2125,10 @@ 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)]; |
- } |
- } |
+ NULL)); |
+ |
+ GLC(context_, context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, |
+ 0)); |
if (do_workaround) { |
// Clean up. |
@@ -2083,9 +2138,97 @@ void GLRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) { |
GLC(context_, context_->deleteTexture(temporary_texture)); |
} |
+ base::Closure finished_callback = |
+ base::Bind(&GLRenderer::FinishedReadback, |
+ base::Unretained(this), |
+ cleanup_callback, |
+ buffer, |
+ dest_pixels, |
+ rect.size()); |
+ // Save the finished_callback so it can be cancelled. |
+ pending_async_read_pixels_.front()->finished_read_pixels_callback.Reset( |
+ finished_callback); |
+ |
+ // Save the buffer to verify the callbacks happen in the expected order. |
+ pending_async_read_pixels_.front()->buffer = buffer; |
+ |
+ if (is_async) { |
+ unsigned sync_point = context_->insertSyncPoint(); |
+ SyncPointHelper::SignalSyncPoint( |
+ context_, |
+ sync_point, |
+ finished_callback); |
+ } else { |
+ resource_provider_->Finish(); |
+ finished_callback.Run(); |
+ } |
+ |
EnforceMemoryPolicy(); |
} |
+void GLRenderer::FinishedReadback( |
+ const AsyncGetFramebufferPixelsCleanupCallback& cleanup_callback, |
+ unsigned source_buffer, |
+ uint8* dest_pixels, |
+ gfx::Size size) { |
+ DCHECK(!pending_async_read_pixels_.empty()); |
+ DCHECK_EQ(source_buffer, pending_async_read_pixels_.back()->buffer); |
+ |
+ uint8* src_pixels = NULL; |
+ |
+ 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) { |
+ 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)]; |
+ } |
+ } |
+ |
+ GLC(context_, context_->unmapBufferCHROMIUM( |
+ GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM)); |
+ } |
+ GLC(context_, context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, |
+ 0)); |
+ GLC(context_, context_->deleteBuffer(source_buffer)); |
+ } |
+ |
+ // 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(src_pixels != NULL); |
+ |
+ 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() && |