Home | History | Annotate | Download | only in compositor
      1 // Copyright 2014 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/browser/compositor/buffer_queue.h"
      6 
      7 #include "content/browser/compositor/image_transport_factory.h"
      8 #include "content/common/gpu/client/context_provider_command_buffer.h"
      9 #include "content/common/gpu/client/gl_helper.h"
     10 #include "gpu/GLES2/gl2extchromium.h"
     11 #include "gpu/command_buffer/client/gles2_interface.h"
     12 #include "third_party/skia/include/core/SkRect.h"
     13 #include "third_party/skia/include/core/SkRegion.h"
     14 
     15 namespace content {
     16 
     17 BufferQueue::BufferQueue(scoped_refptr<cc::ContextProvider> context_provider,
     18                          unsigned int internalformat)
     19     : context_provider_(context_provider),
     20       fbo_(0),
     21       allocated_count_(0),
     22       internalformat_(internalformat) {
     23 }
     24 
     25 BufferQueue::~BufferQueue() {
     26   FreeAllSurfaces();
     27 
     28   gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
     29   if (fbo_)
     30     gl->DeleteFramebuffers(1, &fbo_);
     31 }
     32 
     33 bool BufferQueue::Initialize() {
     34   gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
     35   gl->GenFramebuffers(1, &fbo_);
     36   return fbo_ != 0;
     37 }
     38 
     39 void BufferQueue::BindFramebuffer() {
     40   gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
     41   gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
     42 
     43   if (!current_surface_.texture) {
     44     current_surface_ = GetNextSurface();
     45     gl->FramebufferTexture2D(GL_FRAMEBUFFER,
     46                              GL_COLOR_ATTACHMENT0,
     47                              GL_TEXTURE_2D,
     48                              current_surface_.texture,
     49                              0);
     50   }
     51 }
     52 
     53 void BufferQueue::CopyBufferDamage(int texture,
     54                                    int source_texture,
     55                                    const gfx::Rect& new_damage,
     56                                    const gfx::Rect& old_damage) {
     57   ImageTransportFactory::GetInstance()->GetGLHelper()->CopySubBufferDamage(
     58       texture,
     59       source_texture,
     60       SkRegion(SkIRect::MakeXYWH(new_damage.x(),
     61                                  new_damage.y(),
     62                                  new_damage.width(),
     63                                  new_damage.height())),
     64       SkRegion(SkIRect::MakeXYWH(old_damage.x(),
     65                                  old_damage.y(),
     66                                  old_damage.width(),
     67                                  old_damage.height())));
     68 }
     69 
     70 void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) {
     71   for (size_t i = 0; i < available_surfaces_.size(); i++)
     72     available_surfaces_[i].damage.Union(damage);
     73   for (std::deque<AllocatedSurface>::iterator it =
     74            in_flight_surfaces_.begin();
     75        it != in_flight_surfaces_.end();
     76        ++it)
     77     it->damage.Union(damage);
     78 }
     79 
     80 void BufferQueue::SwapBuffers(const gfx::Rect& damage) {
     81   if (damage != gfx::Rect(size_)) {
     82     // We must have a frame available to copy from.
     83     DCHECK(!in_flight_surfaces_.empty());
     84     CopyBufferDamage(current_surface_.texture,
     85                      in_flight_surfaces_.back().texture,
     86                      damage,
     87                      current_surface_.damage);
     88   }
     89   UpdateBufferDamage(damage);
     90   current_surface_.damage = gfx::Rect();
     91   in_flight_surfaces_.push_back(current_surface_);
     92   current_surface_.texture = 0;
     93   current_surface_.image = 0;
     94 }
     95 
     96 void BufferQueue::Reshape(const gfx::Size& size, float scale_factor) {
     97   DCHECK(!current_surface_.texture);
     98   if (size == size_)
     99     return;
    100   size_ = size;
    101 
    102   // TODO: add stencil buffer when needed.
    103   gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
    104   gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
    105   gl->FramebufferTexture2D(
    106       GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
    107 
    108   FreeAllSurfaces();
    109 }
    110 
    111 void BufferQueue::PageFlipComplete() {
    112   if (in_flight_surfaces_.size() > 1) {
    113     available_surfaces_.push_back(in_flight_surfaces_.front());
    114     in_flight_surfaces_.pop_front();
    115   }
    116 }
    117 
    118 void BufferQueue::FreeAllSurfaces() {
    119   FreeSurface(&current_surface_);
    120   while (!in_flight_surfaces_.empty()) {
    121     FreeSurface(&in_flight_surfaces_.front());
    122     in_flight_surfaces_.pop_front();
    123   }
    124   for (size_t i = 0; i < available_surfaces_.size(); i++)
    125     FreeSurface(&available_surfaces_[i]);
    126   available_surfaces_.clear();
    127 }
    128 
    129 void BufferQueue::FreeSurface(AllocatedSurface* surface) {
    130   if (surface->texture) {
    131     gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
    132     gl->BindTexture(GL_TEXTURE_2D, surface->texture);
    133     gl->ReleaseTexImage2DCHROMIUM(GL_TEXTURE_2D, surface->image);
    134     gl->DeleteTextures(1, &surface->texture);
    135     gl->DestroyImageCHROMIUM(surface->image);
    136     surface->image = 0;
    137     surface->texture = 0;
    138     allocated_count_--;
    139   }
    140 }
    141 
    142 BufferQueue::AllocatedSurface BufferQueue::GetNextSurface() {
    143   if (!available_surfaces_.empty()) {
    144     AllocatedSurface surface = available_surfaces_.back();
    145     available_surfaces_.pop_back();
    146     return surface;
    147   }
    148 
    149   unsigned int texture = 0;
    150   gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
    151   gl->GenTextures(1, &texture);
    152   if (!texture)
    153     return AllocatedSurface();
    154 
    155   // We don't want to allow anything more than triple buffering.
    156   DCHECK_LT(allocated_count_, 4U);
    157 
    158   unsigned int id = gl->CreateImageCHROMIUM(
    159       size_.width(),
    160       size_.height(),
    161       internalformat_,
    162       GL_IMAGE_SCANOUT_CHROMIUM);
    163   if (!id) {
    164     LOG(ERROR) << "Failed to allocate backing CreateImageCHROMIUM surface";
    165     gl->DeleteTextures(1, &texture);
    166     return AllocatedSurface();
    167   }
    168   allocated_count_++;
    169   gl->BindTexture(GL_TEXTURE_2D, texture);
    170   gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, id);
    171   return AllocatedSurface(texture, id, gfx::Rect(size_));
    172 }
    173 
    174 }  // namespace content
    175