// 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 "content/renderer/gpu/frame_swap_message_queue.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_t routing_id, uint32_t output_surface_id, const scoped_refptr& context_provider, const scoped_refptr& worker_context_provider, scoped_refptr swap_frame_message_queue, cc::ResourceFormat format) : CompositorOutputSurface(routing_id, output_surface_id, context_provider, worker_context_provider, nullptr, swap_frame_message_queue, true), fbo_(0), is_backbuffer_discarded_(false), format_(format) { pending_textures_.push_back(TransferableFrame()); capabilities_.uses_default_gl_framebuffer = false; } MailboxOutputSurface::~MailboxOutputSurface() {} void MailboxOutputSurface::DetachFromClient() { 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(); } cc::OutputSurface::DetachFromClient(); } 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_token.HasData()) gl->WaitSyncTokenCHROMIUM(current_backing_.sync_token.GetConstData()); 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, bool alpha) { 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_t 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::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_token = ack.gl_frame_data->sync_token; 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_t 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_->ContextGL()->GetGraphicsResetStatusKHR() != GL_NO_ERROR); frame->gl_frame_data->mailbox = current_backing_.mailbox; gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); const GLuint64 fence_sync = gl->InsertFenceSyncCHROMIUM(); gl->Flush(); gl->GenSyncTokenCHROMIUM(fence_sync, frame->gl_frame_data->sync_token.GetData()); 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; } MailboxOutputSurface::TransferableFrame::TransferableFrame() : texture_id(0) {} MailboxOutputSurface::TransferableFrame::TransferableFrame( uint32_t texture_id, const gpu::Mailbox& mailbox, const gfx::Size size) : texture_id(texture_id), mailbox(mailbox), size(size) {} } // namespace content