| 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 c2733a71976962dc279db27dcc3109506c32a354..6da208557a17bced23950b6551a61586074add6b 100644
|
| --- a/content/browser/renderer_host/compositing_iosurface_mac.mm
|
| +++ b/content/browser/renderer_host/compositing_iosurface_mac.mm
|
| @@ -6,7 +6,6 @@
|
|
|
| #include <OpenGL/CGLRenderers.h>
|
| #include <OpenGL/OpenGL.h>
|
| -#include <vector>
|
|
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| @@ -163,8 +162,11 @@ CVReturn DisplayLinkCallback(CVDisplayLinkRef display_link,
|
| return kCVReturnSuccess;
|
| }
|
|
|
| -CompositingIOSurfaceMac::CopyContext::CopyContext()
|
| - : num_outputs(0),
|
| +CompositingIOSurfaceMac::CopyContext::CopyContext(
|
| + const scoped_refptr<CompositingIOSurfaceContext>& context)
|
| + : transformer(new CompositingIOSurfaceTransformer(
|
| + GL_TEXTURE_RECTANGLE_ARB, true, context->shader_program_cache())),
|
| + num_outputs(0),
|
| fence(0),
|
| cycles_elapsed(0) {
|
| memset(output_textures, 0, sizeof(output_textures));
|
| @@ -173,21 +175,52 @@ CompositingIOSurfaceMac::CopyContext::CopyContext()
|
| }
|
|
|
| CompositingIOSurfaceMac::CopyContext::~CopyContext() {
|
| + DCHECK_EQ(frame_buffers[0], 0u) << "Failed to call ReleaseCachedGLObjects().";
|
| }
|
|
|
| -void CompositingIOSurfaceMac::CopyContext::CleanUp() {
|
| - glDeleteFramebuffersEXT(num_outputs, frame_buffers); CHECK_GL_ERROR();
|
| - glDeleteTextures(num_outputs, output_textures);
|
| - CHECK_GL_ERROR();
|
| +void CompositingIOSurfaceMac::CopyContext::ReleaseCachedGLObjects() {
|
| + // No outstanding callbacks should be pending.
|
| + DCHECK(map_buffer_callback.is_null());
|
| + DCHECK(done_callback.is_null());
|
|
|
| // For an asynchronous read-back, there are more objects to delete:
|
| if (fence) {
|
| - glDeleteBuffers(num_outputs, pixel_buffers);
|
| - CHECK_GL_ERROR();
|
| + glDeleteBuffers(arraysize(pixel_buffers), pixel_buffers); CHECK_GL_ERROR();
|
| + memset(pixel_buffers, 0, sizeof(pixel_buffers));
|
| glDeleteFencesAPPLE(1, &fence); CHECK_GL_ERROR();
|
| + fence = 0;
|
| + }
|
| +
|
| + glDeleteFramebuffersEXT(arraysize(frame_buffers), frame_buffers);
|
| + CHECK_GL_ERROR();
|
| + memset(frame_buffers, 0, sizeof(frame_buffers));
|
| +
|
| + // Note: |output_textures| are owned by the transformer.
|
| + if (transformer)
|
| + transformer->ReleaseCachedGLObjects();
|
| +}
|
| +
|
| +void CompositingIOSurfaceMac::CopyContext::PrepareReadbackFramebuffers() {
|
| + for (int i = 0; i < num_outputs; ++i) {
|
| + if (!frame_buffers[i]) {
|
| + glGenFramebuffersEXT(1, &frame_buffers[i]); CHECK_GL_ERROR();
|
| + }
|
| }
|
| }
|
|
|
| +void CompositingIOSurfaceMac::CopyContext::PrepareForAsynchronousReadback() {
|
| + PrepareReadbackFramebuffers();
|
| + if (!fence) {
|
| + glGenFencesAPPLE(1, &fence); CHECK_GL_ERROR();
|
| + }
|
| + for (int i = 0; i < num_outputs; ++i) {
|
| + if (!pixel_buffers[i]) {
|
| + glGenBuffersARB(1, &pixel_buffers[i]); CHECK_GL_ERROR();
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| // static
|
| CompositingIOSurfaceMac* CompositingIOSurfaceMac::Create(
|
| int window_number,
|
| @@ -264,9 +297,15 @@ void CompositingIOSurfaceMac::SwitchToContextOnNewWindow(int window_number) {
|
| return;
|
|
|
| // Asynchronous copies must complete in the same context they started in,
|
| - // defer updating the GL context to the new window until the copy finishes.
|
| + // defer updating the GL context to the new window until the copy finishes and
|
| + // all outstanding CopyContexts are destroyed.
|
| if (!copy_requests_.empty())
|
| return;
|
| + if (!copy_context_pool_.empty()) {
|
| + CGLSetCurrentContext(context_->cgl_context());
|
| + DestroyAllCopyContextsWithinContext();
|
| + CGLSetCurrentContext(0);
|
| + }
|
|
|
| scoped_refptr<CompositingIOSurfaceContext> new_context =
|
| CompositingIOSurfaceContext::Get(window_number,
|
| @@ -274,7 +313,6 @@ void CompositingIOSurfaceMac::SwitchToContextOnNewWindow(int window_number) {
|
| if (!new_context)
|
| return;
|
|
|
| - transformer_.reset(nil);
|
| context_ = new_context;
|
| }
|
|
|
| @@ -295,7 +333,7 @@ CompositingIOSurfaceMac::~CompositingIOSurfaceMac() {
|
| FailAllCopies();
|
| CVDisplayLinkRelease(display_link_);
|
| CGLSetCurrentContext(context_->cgl_context());
|
| - CleanupAllCopiesWithinContext();
|
| + DestroyAllCopyContextsWithinContext();
|
| UnrefIOSurfaceWithContextCurrent();
|
| CGLSetCurrentContext(0);
|
| context_ = nil;
|
| @@ -724,51 +762,56 @@ base::Closure CompositingIOSurfaceMac::CopyToSelectedOutputWithinContext(
|
| "output", bitmap_output ? "SkBitmap (ARGB)" : "VideoFrame (YV12)",
|
| "async_readback", async_copy);
|
|
|
| + CopyContext* copy_context;
|
| + if (copy_context_pool_.empty()) {
|
| + // TODO(miu): Determine appropriate limit once new async copy approach is
|
| + // put in place.
|
| + if (copy_requests_.size() >= 3)
|
| + return base::Bind(done_callback, false);
|
| + copy_context = new CopyContext(context_);
|
| + } else {
|
| + copy_context = copy_context_pool_.back();
|
| + copy_context_pool_.pop_back();
|
| + }
|
| +
|
| // Set up source texture, bound to the GL_TEXTURE_RECTANGLE_ARB target.
|
| if (!MapIOSurfaceToTexture(io_surface_handle_))
|
| return base::Bind(done_callback, false);
|
|
|
| - // Create the transformer_ on first use.
|
| - if (!transformer_) {
|
| - transformer_.reset(new CompositingIOSurfaceTransformer(
|
| - GL_TEXTURE_RECTANGLE_ARB,
|
| - true,
|
| - context_->shader_program_cache()));
|
| - }
|
| -
|
| // Send transform commands to the GPU.
|
| const gfx::Rect src_rect = IntersectWithIOSurface(src_pixel_subrect,
|
| src_scale_factor);
|
| - CopyContext copy_context;
|
| + copy_context->num_outputs = 0;
|
| if (bitmap_output) {
|
| - if (transformer_->ResizeBilinear(texture_, src_rect, dst_pixel_rect.size(),
|
| - ©_context.output_textures[0])) {
|
| - copy_context.num_outputs = 1;
|
| - copy_context.output_texture_sizes[0] = dst_pixel_rect.size();
|
| + if (copy_context->transformer->ResizeBilinear(
|
| + texture_, src_rect, dst_pixel_rect.size(),
|
| + ©_context->output_textures[0])) {
|
| + copy_context->num_outputs = 1;
|
| + copy_context->output_texture_sizes[0] = dst_pixel_rect.size();
|
| }
|
| } else {
|
| - if (transformer_->TransformRGBToYV12(
|
| + if (copy_context->transformer->TransformRGBToYV12(
|
| texture_, src_rect, dst_pixel_rect.size(),
|
| - ©_context.output_textures[0],
|
| - ©_context.output_textures[1],
|
| - ©_context.output_textures[2],
|
| - ©_context.output_texture_sizes[0],
|
| - ©_context.output_texture_sizes[1])) {
|
| - copy_context.num_outputs = 3;
|
| - copy_context.output_texture_sizes[2] =
|
| - copy_context.output_texture_sizes[1];
|
| + ©_context->output_textures[0],
|
| + ©_context->output_textures[1],
|
| + ©_context->output_textures[2],
|
| + ©_context->output_texture_sizes[0],
|
| + ©_context->output_texture_sizes[1])) {
|
| + copy_context->num_outputs = 3;
|
| + copy_context->output_texture_sizes[2] =
|
| + copy_context->output_texture_sizes[1];
|
| }
|
| }
|
| - if (!copy_context.num_outputs)
|
| + if (!copy_context->num_outputs)
|
| return base::Bind(done_callback, false);
|
|
|
| // In the asynchronous case, issue commands to the GPU and return a null
|
| // closure here. In the synchronous case, perform a blocking readback and
|
| // return a callback to be run outside the CGL context to indicate success.
|
| if (async_copy) {
|
| - copy_context.done_callback = done_callback;
|
| + copy_context->done_callback = done_callback;
|
| AsynchronousReadbackForCopy(
|
| - dst_pixel_rect, called_within_draw, ©_context, bitmap_output,
|
| + dst_pixel_rect, called_within_draw, copy_context, bitmap_output,
|
| video_frame_output);
|
| copy_requests_.push_back(copy_context);
|
| if (!finish_copy_timer_.IsRunning())
|
| @@ -776,7 +819,7 @@ base::Closure CompositingIOSurfaceMac::CopyToSelectedOutputWithinContext(
|
| return base::Closure();
|
| } else {
|
| const bool success = SynchronousReadbackForCopy(
|
| - dst_pixel_rect, ©_context, bitmap_output, video_frame_output);
|
| + dst_pixel_rect, copy_context, bitmap_output, video_frame_output);
|
| return base::Bind(done_callback, success);
|
| }
|
| }
|
| @@ -787,13 +830,9 @@ void CompositingIOSurfaceMac::AsynchronousReadbackForCopy(
|
| CopyContext* copy_context,
|
| const SkBitmap* bitmap_output,
|
| const scoped_refptr<media::VideoFrame>& video_frame_output) {
|
| - glGenFencesAPPLE(1, ©_context->fence); CHECK_GL_ERROR();
|
| + copy_context->PrepareForAsynchronousReadback();
|
|
|
| - // Copy the textures to a PBO.
|
| - glGenFramebuffersEXT(copy_context->num_outputs, copy_context->frame_buffers);
|
| - CHECK_GL_ERROR();
|
| - glGenBuffersARB(copy_context->num_outputs, copy_context->pixel_buffers);
|
| - CHECK_GL_ERROR();
|
| + // Copy the textures to their corresponding PBO.
|
| for (int i = 0; i < copy_context->num_outputs; ++i) {
|
| TRACE_EVENT1(
|
| "browser", "CompositingIOSurfaceMac::AsynchronousReadbackForCopy",
|
| @@ -824,8 +863,9 @@ void CompositingIOSurfaceMac::AsynchronousReadbackForCopy(
|
| glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); CHECK_GL_ERROR();
|
|
|
| glSetFenceAPPLE(copy_context->fence); CHECK_GL_ERROR();
|
| + copy_context->cycles_elapsed = 0;
|
|
|
| - // When this asynchronous copy happens in a draw operaton there is not need
|
| + // When this asynchronous copy happens in a draw operaton there is no need
|
| // to explicitly flush because there will be a swap buffer and this flush
|
| // hurts performance.
|
| if (!called_within_draw) {
|
| @@ -849,15 +889,15 @@ void CompositingIOSurfaceMac::FinishAllCopies() {
|
| void CompositingIOSurfaceMac::FinishAllCopiesWithinContext(
|
| std::vector<base::Closure>* done_callbacks) {
|
| while (!copy_requests_.empty()) {
|
| - CopyContext& copy_context = copy_requests_.front();
|
| + CopyContext* const copy_context = copy_requests_.front();
|
|
|
| - if (copy_context.fence) {
|
| - const bool copy_completed = glTestFenceAPPLE(copy_context.fence);
|
| + if (copy_context->fence) {
|
| + const bool copy_completed = glTestFenceAPPLE(copy_context->fence);
|
| CHECK_GL_ERROR();
|
|
|
| if (!copy_completed &&
|
| - copy_context.cycles_elapsed < kFinishCopyRetryCycles) {
|
| - ++copy_context.cycles_elapsed;
|
| + copy_context->cycles_elapsed < kFinishCopyRetryCycles) {
|
| + ++copy_context->cycles_elapsed;
|
| // This copy has not completed there is no need to test subsequent
|
| // requests.
|
| break;
|
| @@ -865,24 +905,26 @@ void CompositingIOSurfaceMac::FinishAllCopiesWithinContext(
|
| }
|
|
|
| bool success = true;
|
| - for (int i = 0; success && i < copy_context.num_outputs; ++i) {
|
| + for (int i = 0; success && i < copy_context->num_outputs; ++i) {
|
| TRACE_EVENT1(
|
| "browser", "CompositingIOSurfaceMac::FinishAllCopyWithinContext",
|
| "plane", i);
|
|
|
| - glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, copy_context.pixel_buffers[i]);
|
| + glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, copy_context->pixel_buffers[i]);
|
| CHECK_GL_ERROR();
|
|
|
| void* buf = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
|
| CHECK_GL_ERROR();
|
| - success &= copy_context.map_buffer_callback.Run(buf, i);
|
| + success &= copy_context->map_buffer_callback.Run(buf, i);
|
| glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); CHECK_GL_ERROR();
|
| }
|
| -
|
| + copy_context->map_buffer_callback.Reset();
|
| glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); CHECK_GL_ERROR();
|
| - copy_context.CleanUp();
|
| - done_callbacks->push_back(base::Bind(copy_context.done_callback, success));
|
| +
|
| copy_requests_.pop_front();
|
| + done_callbacks->push_back(base::Bind(copy_context->done_callback, success));
|
| + copy_context->done_callback.Reset();
|
| + copy_context_pool_.push_back(copy_context);
|
| }
|
| if (copy_requests_.empty())
|
| finish_copy_timer_.Stop();
|
| @@ -894,8 +936,7 @@ bool CompositingIOSurfaceMac::SynchronousReadbackForCopy(
|
| const SkBitmap* bitmap_output,
|
| const scoped_refptr<media::VideoFrame>& video_frame_output) {
|
| bool success = true;
|
| - glGenFramebuffersEXT(copy_context->num_outputs, copy_context->frame_buffers);
|
| - CHECK_GL_ERROR();
|
| + copy_context->PrepareReadbackFramebuffers();
|
| for (int i = 0; i < copy_context->num_outputs; ++i) {
|
| TRACE_EVENT1(
|
| "browser", "CompositingIOSurfaceMac::SynchronousReadbackForCopy",
|
| @@ -962,19 +1003,34 @@ bool CompositingIOSurfaceMac::SynchronousReadbackForCopy(
|
| }
|
|
|
| glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); CHECK_GL_ERROR();
|
| - copy_context->CleanUp();
|
| + copy_context_pool_.push_back(copy_context);
|
| return success;
|
| }
|
|
|
| -void CompositingIOSurfaceMac::CleanupAllCopiesWithinContext() {
|
| - for (size_t i = 0; i < copy_requests_.size(); ++i)
|
| - copy_requests_[i].CleanUp();
|
| - copy_requests_.clear();
|
| +void CompositingIOSurfaceMac::FailAllCopies() {
|
| + for (size_t i = 0; i < copy_requests_.size(); ++i) {
|
| + copy_requests_[i]->map_buffer_callback.Reset();
|
| +
|
| + base::Callback<void(bool)>& done_callback =
|
| + copy_requests_[i]->done_callback;
|
| + if (!done_callback.is_null()) {
|
| + done_callback.Run(false);
|
| + done_callback.Reset();
|
| + }
|
| + }
|
| }
|
|
|
| -void CompositingIOSurfaceMac::FailAllCopies() {
|
| - for (size_t i = 0; i < copy_requests_.size(); ++i)
|
| - copy_requests_[i].done_callback.Run(false);
|
| +void CompositingIOSurfaceMac::DestroyAllCopyContextsWithinContext() {
|
| + // Move all in-flight copies, if any, back into the pool. Then, destroy all
|
| + // the CopyContexts in the pool.
|
| + copy_context_pool_.insert(copy_context_pool_.end(),
|
| + copy_requests_.begin(), copy_requests_.end());
|
| + copy_requests_.clear();
|
| + while (!copy_context_pool_.empty()) {
|
| + scoped_ptr<CopyContext> copy_context(copy_context_pool_.back());
|
| + copy_context_pool_.pop_back();
|
| + copy_context->ReleaseCachedGLObjects();
|
| + }
|
| }
|
|
|
| gfx::Rect CompositingIOSurfaceMac::IntersectWithIOSurface(
|
|
|