| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/gpu/mailbox_output_surface.h" |
| |
| #include "base/logging.h" |
| #include "cc/output/compositor_frame.h" |
| #include "cc/output/compositor_frame_ack.h" |
| #include "cc/output/gl_frame_data.h" |
| #include "cc/resources/resource_provider.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "third_party/khronos/GLES2/gl2.h" |
| #include "third_party/khronos/GLES2/gl2ext.h" |
| |
| using cc::CompositorFrame; |
| using cc::GLFrameData; |
| using cc::ResourceProvider; |
| using gpu::Mailbox; |
| using gpu::gles2::GLES2Interface; |
| |
| namespace content { |
| |
| MailboxOutputSurface::MailboxOutputSurface( |
| int32 routing_id, |
| uint32 output_surface_id, |
| const scoped_refptr<ContextProviderCommandBuffer>& context_provider, |
| scoped_ptr<cc::SoftwareOutputDevice> software_device, |
| cc::ResourceFormat format) |
| : CompositorOutputSurface(routing_id, |
| output_surface_id, |
| context_provider, |
| software_device.Pass(), |
| true), |
| fbo_(0), |
| is_backbuffer_discarded_(false), |
| format_(format) { |
| pending_textures_.push_back(TransferableFrame()); |
| capabilities_.max_frames_pending = 1; |
| capabilities_.uses_default_gl_framebuffer = false; |
| } |
| |
| MailboxOutputSurface::~MailboxOutputSurface() { |
| DiscardBackbuffer(); |
| while (!pending_textures_.empty()) { |
| if (pending_textures_.front().texture_id) { |
| context_provider_->ContextGL()->DeleteTextures( |
| 1, &pending_textures_.front().texture_id); |
| } |
| pending_textures_.pop_front(); |
| } |
| } |
| |
| void MailboxOutputSurface::EnsureBackbuffer() { |
| is_backbuffer_discarded_ = false; |
| |
| GLES2Interface* gl = context_provider_->ContextGL(); |
| |
| if (!current_backing_.texture_id) { |
| // Find a texture of matching size to recycle. |
| while (!returned_textures_.empty()) { |
| TransferableFrame& texture = returned_textures_.front(); |
| if (texture.size == surface_size_) { |
| current_backing_ = texture; |
| if (current_backing_.sync_point) |
| gl->WaitSyncPointCHROMIUM(current_backing_.sync_point); |
| returned_textures_.pop(); |
| break; |
| } |
| |
| gl->DeleteTextures(1, &texture.texture_id); |
| returned_textures_.pop(); |
| } |
| |
| if (!current_backing_.texture_id) { |
| gl->GenTextures(1, ¤t_backing_.texture_id); |
| current_backing_.size = surface_size_; |
| gl->BindTexture(GL_TEXTURE_2D, current_backing_.texture_id); |
| gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| gl->TexImage2D(GL_TEXTURE_2D, |
| 0, |
| GLInternalFormat(format_), |
| surface_size_.width(), |
| surface_size_.height(), |
| 0, |
| GLDataFormat(format_), |
| GLDataType(format_), |
| NULL); |
| gl->GenMailboxCHROMIUM(current_backing_.mailbox.name); |
| gl->ProduceTextureCHROMIUM(GL_TEXTURE_2D, current_backing_.mailbox.name); |
| } |
| } |
| } |
| |
| void MailboxOutputSurface::DiscardBackbuffer() { |
| is_backbuffer_discarded_ = true; |
| |
| GLES2Interface* gl = context_provider_->ContextGL(); |
| |
| if (current_backing_.texture_id) { |
| gl->DeleteTextures(1, ¤t_backing_.texture_id); |
| current_backing_ = TransferableFrame(); |
| } |
| |
| while (!returned_textures_.empty()) { |
| const TransferableFrame& frame = returned_textures_.front(); |
| gl->DeleteTextures(1, &frame.texture_id); |
| returned_textures_.pop(); |
| } |
| |
| if (fbo_) { |
| gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); |
| gl->DeleteFramebuffers(1, &fbo_); |
| fbo_ = 0; |
| } |
| } |
| |
| void MailboxOutputSurface::Reshape(const gfx::Size& size, float scale_factor) { |
| if (size == surface_size_) |
| return; |
| |
| surface_size_ = size; |
| device_scale_factor_ = scale_factor; |
| DiscardBackbuffer(); |
| EnsureBackbuffer(); |
| } |
| |
| void MailboxOutputSurface::BindFramebuffer() { |
| EnsureBackbuffer(); |
| DCHECK(current_backing_.texture_id); |
| |
| GLES2Interface* gl = context_provider_->ContextGL(); |
| |
| if (!fbo_) |
| gl->GenFramebuffers(1, &fbo_); |
| gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); |
| gl->FramebufferTexture2D(GL_FRAMEBUFFER, |
| GL_COLOR_ATTACHMENT0, |
| GL_TEXTURE_2D, |
| current_backing_.texture_id, |
| 0); |
| } |
| |
| void MailboxOutputSurface::OnSwapAck(uint32 output_surface_id, |
| const cc::CompositorFrameAck& ack) { |
| // Ignore message if it's a stale one coming from a different output surface |
| // (e.g. after a lost context). |
| if (output_surface_id != output_surface_id_) { |
| CompositorOutputSurface::OnSwapAck(output_surface_id, ack); |
| return; |
| } |
| if (!ack.gl_frame_data->mailbox.IsZero()) { |
| DCHECK(!ack.gl_frame_data->size.IsEmpty()); |
| // The browser could be returning the oldest or any other pending texture |
| // if it decided to skip a frame. |
| std::deque<TransferableFrame>::iterator it; |
| for (it = pending_textures_.begin(); it != pending_textures_.end(); it++) { |
| DCHECK(!it->mailbox.IsZero()); |
| if (!memcmp(it->mailbox.name, |
| ack.gl_frame_data->mailbox.name, |
| sizeof(it->mailbox.name))) { |
| DCHECK(it->size == ack.gl_frame_data->size); |
| break; |
| } |
| } |
| DCHECK(it != pending_textures_.end()); |
| it->sync_point = ack.gl_frame_data->sync_point; |
| |
| if (!is_backbuffer_discarded_) { |
| returned_textures_.push(*it); |
| } else { |
| context_provider_->ContextGL()->DeleteTextures(1, &it->texture_id); |
| } |
| |
| pending_textures_.erase(it); |
| } else { |
| DCHECK(!pending_textures_.empty()); |
| // The browser always keeps one texture as the frontbuffer. |
| // If it does not return a mailbox, it discarded the frontbuffer which is |
| // the oldest texture we sent. |
| uint32 texture_id = pending_textures_.front().texture_id; |
| if (texture_id) |
| context_provider_->ContextGL()->DeleteTextures(1, &texture_id); |
| pending_textures_.pop_front(); |
| } |
| CompositorOutputSurface::OnSwapAck(output_surface_id, ack); |
| } |
| |
| void MailboxOutputSurface::SwapBuffers(cc::CompositorFrame* frame) { |
| DCHECK(frame->gl_frame_data); |
| DCHECK(!surface_size_.IsEmpty()); |
| DCHECK(surface_size_ == current_backing_.size); |
| DCHECK(frame->gl_frame_data->size == current_backing_.size); |
| DCHECK(!current_backing_.mailbox.IsZero() || |
| context_provider_->IsContextLost()); |
| |
| frame->gl_frame_data->mailbox = current_backing_.mailbox; |
| context_provider_->ContextGL()->Flush(); |
| frame->gl_frame_data->sync_point = |
| context_provider_->ContextGL()->InsertSyncPointCHROMIUM(); |
| CompositorOutputSurface::SwapBuffers(frame); |
| |
| pending_textures_.push_back(current_backing_); |
| current_backing_ = TransferableFrame(); |
| } |
| |
| size_t MailboxOutputSurface::GetNumAcksPending() { |
| DCHECK(pending_textures_.size()); |
| return pending_textures_.size() - 1; |
| } |
| |
| } // namespace content |