Home | History | Annotate | Download | only in gpu
      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 
     17 using cc::CompositorFrame;
     18 using cc::GLFrameData;
     19 using cc::ResourceProvider;
     20 using gpu::Mailbox;
     21 using gpu::gles2::GLES2Interface;
     22 
     23 namespace content {
     24 
     25 MailboxOutputSurface::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 
     46 MailboxOutputSurface::~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 
     57 void 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, &current_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 
    101 void 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, &current_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 
    124 void 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 
    134 void 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 
    150 void 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 
    195 void 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 
    213 size_t MailboxOutputSurface::GetNumAcksPending() {
    214   DCHECK(pending_textures_.size());
    215   return pending_textures_.size() - 1;
    216 }
    217 
    218 }  // namespace content
    219