1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/renderer/gpu/mailbox_output_surface.h" 6 7#include "base/logging.h" 8#include "cc/output/compositor_frame.h" 9#include "cc/output/compositor_frame_ack.h" 10#include "cc/output/gl_frame_data.h" 11#include "cc/resources/resource_provider.h" 12#include "content/renderer/gpu/frame_swap_message_queue.h" 13#include "gpu/command_buffer/client/gles2_interface.h" 14#include "third_party/khronos/GLES2/gl2.h" 15#include "third_party/khronos/GLES2/gl2ext.h" 16 17using cc::CompositorFrame; 18using cc::GLFrameData; 19using cc::ResourceProvider; 20using gpu::Mailbox; 21using gpu::gles2::GLES2Interface; 22 23namespace content { 24 25MailboxOutputSurface::MailboxOutputSurface( 26 int32 routing_id, 27 uint32 output_surface_id, 28 const scoped_refptr<ContextProviderCommandBuffer>& context_provider, 29 scoped_ptr<cc::SoftwareOutputDevice> software_device, 30 scoped_refptr<FrameSwapMessageQueue> swap_frame_message_queue, 31 cc::ResourceFormat format) 32 : CompositorOutputSurface(routing_id, 33 output_surface_id, 34 context_provider, 35 software_device.Pass(), 36 swap_frame_message_queue, 37 true), 38 fbo_(0), 39 is_backbuffer_discarded_(false), 40 format_(format) { 41 pending_textures_.push_back(TransferableFrame()); 42 capabilities_.max_frames_pending = 1; 43 capabilities_.uses_default_gl_framebuffer = false; 44} 45 46MailboxOutputSurface::~MailboxOutputSurface() { 47 DiscardBackbuffer(); 48 while (!pending_textures_.empty()) { 49 if (pending_textures_.front().texture_id) { 50 context_provider_->ContextGL()->DeleteTextures( 51 1, &pending_textures_.front().texture_id); 52 } 53 pending_textures_.pop_front(); 54 } 55} 56 57void MailboxOutputSurface::EnsureBackbuffer() { 58 is_backbuffer_discarded_ = false; 59 60 GLES2Interface* gl = context_provider_->ContextGL(); 61 62 if (!current_backing_.texture_id) { 63 // Find a texture of matching size to recycle. 64 while (!returned_textures_.empty()) { 65 TransferableFrame& texture = returned_textures_.front(); 66 if (texture.size == surface_size_) { 67 current_backing_ = texture; 68 if (current_backing_.sync_point) 69 gl->WaitSyncPointCHROMIUM(current_backing_.sync_point); 70 returned_textures_.pop(); 71 break; 72 } 73 74 gl->DeleteTextures(1, &texture.texture_id); 75 returned_textures_.pop(); 76 } 77 78 if (!current_backing_.texture_id) { 79 gl->GenTextures(1, ¤t_backing_.texture_id); 80 current_backing_.size = surface_size_; 81 gl->BindTexture(GL_TEXTURE_2D, current_backing_.texture_id); 82 gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 83 gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 84 gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 85 gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 86 gl->TexImage2D(GL_TEXTURE_2D, 87 0, 88 GLInternalFormat(format_), 89 surface_size_.width(), 90 surface_size_.height(), 91 0, 92 GLDataFormat(format_), 93 GLDataType(format_), 94 NULL); 95 gl->GenMailboxCHROMIUM(current_backing_.mailbox.name); 96 gl->ProduceTextureCHROMIUM(GL_TEXTURE_2D, current_backing_.mailbox.name); 97 } 98 } 99} 100 101void MailboxOutputSurface::DiscardBackbuffer() { 102 is_backbuffer_discarded_ = true; 103 104 GLES2Interface* gl = context_provider_->ContextGL(); 105 106 if (current_backing_.texture_id) { 107 gl->DeleteTextures(1, ¤t_backing_.texture_id); 108 current_backing_ = TransferableFrame(); 109 } 110 111 while (!returned_textures_.empty()) { 112 const TransferableFrame& frame = returned_textures_.front(); 113 gl->DeleteTextures(1, &frame.texture_id); 114 returned_textures_.pop(); 115 } 116 117 if (fbo_) { 118 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); 119 gl->DeleteFramebuffers(1, &fbo_); 120 fbo_ = 0; 121 } 122} 123 124void MailboxOutputSurface::Reshape(const gfx::Size& size, float scale_factor) { 125 if (size == surface_size_) 126 return; 127 128 surface_size_ = size; 129 device_scale_factor_ = scale_factor; 130 DiscardBackbuffer(); 131 EnsureBackbuffer(); 132} 133 134void MailboxOutputSurface::BindFramebuffer() { 135 EnsureBackbuffer(); 136 DCHECK(current_backing_.texture_id); 137 138 GLES2Interface* gl = context_provider_->ContextGL(); 139 140 if (!fbo_) 141 gl->GenFramebuffers(1, &fbo_); 142 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); 143 gl->FramebufferTexture2D(GL_FRAMEBUFFER, 144 GL_COLOR_ATTACHMENT0, 145 GL_TEXTURE_2D, 146 current_backing_.texture_id, 147 0); 148} 149 150void MailboxOutputSurface::OnSwapAck(uint32 output_surface_id, 151 const cc::CompositorFrameAck& ack) { 152 // Ignore message if it's a stale one coming from a different output surface 153 // (e.g. after a lost context). 154 if (output_surface_id != output_surface_id_) { 155 CompositorOutputSurface::OnSwapAck(output_surface_id, ack); 156 return; 157 } 158 if (!ack.gl_frame_data->mailbox.IsZero()) { 159 DCHECK(!ack.gl_frame_data->size.IsEmpty()); 160 // The browser could be returning the oldest or any other pending texture 161 // if it decided to skip a frame. 162 std::deque<TransferableFrame>::iterator it; 163 for (it = pending_textures_.begin(); it != pending_textures_.end(); it++) { 164 DCHECK(!it->mailbox.IsZero()); 165 if (!memcmp(it->mailbox.name, 166 ack.gl_frame_data->mailbox.name, 167 sizeof(it->mailbox.name))) { 168 DCHECK(it->size == ack.gl_frame_data->size); 169 break; 170 } 171 } 172 DCHECK(it != pending_textures_.end()); 173 it->sync_point = ack.gl_frame_data->sync_point; 174 175 if (!is_backbuffer_discarded_) { 176 returned_textures_.push(*it); 177 } else { 178 context_provider_->ContextGL()->DeleteTextures(1, &it->texture_id); 179 } 180 181 pending_textures_.erase(it); 182 } else { 183 DCHECK(!pending_textures_.empty()); 184 // The browser always keeps one texture as the frontbuffer. 185 // If it does not return a mailbox, it discarded the frontbuffer which is 186 // the oldest texture we sent. 187 uint32 texture_id = pending_textures_.front().texture_id; 188 if (texture_id) 189 context_provider_->ContextGL()->DeleteTextures(1, &texture_id); 190 pending_textures_.pop_front(); 191 } 192 CompositorOutputSurface::OnSwapAck(output_surface_id, ack); 193} 194 195void MailboxOutputSurface::SwapBuffers(cc::CompositorFrame* frame) { 196 DCHECK(frame->gl_frame_data); 197 DCHECK(!surface_size_.IsEmpty()); 198 DCHECK(surface_size_ == current_backing_.size); 199 DCHECK(frame->gl_frame_data->size == current_backing_.size); 200 DCHECK(!current_backing_.mailbox.IsZero() || 201 context_provider_->IsContextLost()); 202 203 frame->gl_frame_data->mailbox = current_backing_.mailbox; 204 context_provider_->ContextGL()->Flush(); 205 frame->gl_frame_data->sync_point = 206 context_provider_->ContextGL()->InsertSyncPointCHROMIUM(); 207 CompositorOutputSurface::SwapBuffers(frame); 208 209 pending_textures_.push_back(current_backing_); 210 current_backing_ = TransferableFrame(); 211} 212 213size_t MailboxOutputSurface::GetNumAcksPending() { 214 DCHECK(pending_textures_.size()); 215 return pending_textures_.size() - 1; 216} 217 218} // namespace content 219