// Copyright 2014 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/browser/compositor/buffer_queue.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "content/common/gpu/client/context_provider_command_buffer.h"
#include "content/common/gpu/client/gl_helper.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkRegion.h"
namespace content {
BufferQueue::BufferQueue(scoped_refptr<cc::ContextProvider> context_provider,
unsigned int internalformat)
: context_provider_(context_provider),
fbo_(0),
allocated_count_(0),
internalformat_(internalformat) {
}
BufferQueue::~BufferQueue() {
FreeAllSurfaces();
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
if (fbo_)
gl->DeleteFramebuffers(1, &fbo_);
}
bool BufferQueue::Initialize() {
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
gl->GenFramebuffers(1, &fbo_);
return fbo_ != 0;
}
void BufferQueue::BindFramebuffer() {
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
if (!current_surface_.texture) {
current_surface_ = GetNextSurface();
gl->FramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
current_surface_.texture,
0);
}
}
void BufferQueue::CopyBufferDamage(int texture,
int source_texture,
const gfx::Rect& new_damage,
const gfx::Rect& old_damage) {
ImageTransportFactory::GetInstance()->GetGLHelper()->CopySubBufferDamage(
texture,
source_texture,
SkRegion(SkIRect::MakeXYWH(new_damage.x(),
new_damage.y(),
new_damage.width(),
new_damage.height())),
SkRegion(SkIRect::MakeXYWH(old_damage.x(),
old_damage.y(),
old_damage.width(),
old_damage.height())));
}
void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) {
for (size_t i = 0; i < available_surfaces_.size(); i++)
available_surfaces_[i].damage.Union(damage);
for (std::deque<AllocatedSurface>::iterator it =
in_flight_surfaces_.begin();
it != in_flight_surfaces_.end();
++it)
it->damage.Union(damage);
}
void BufferQueue::SwapBuffers(const gfx::Rect& damage) {
if (damage != gfx::Rect(size_)) {
// We must have a frame available to copy from.
DCHECK(!in_flight_surfaces_.empty());
CopyBufferDamage(current_surface_.texture,
in_flight_surfaces_.back().texture,
damage,
current_surface_.damage);
}
UpdateBufferDamage(damage);
current_surface_.damage = gfx::Rect();
in_flight_surfaces_.push_back(current_surface_);
current_surface_.texture = 0;
current_surface_.image = 0;
}
void BufferQueue::Reshape(const gfx::Size& size, float scale_factor) {
DCHECK(!current_surface_.texture);
if (size == size_)
return;
size_ = size;
// TODO: add stencil buffer when needed.
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
gl->FramebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
FreeAllSurfaces();
}
void BufferQueue::PageFlipComplete() {
if (in_flight_surfaces_.size() > 1) {
available_surfaces_.push_back(in_flight_surfaces_.front());
in_flight_surfaces_.pop_front();
}
}
void BufferQueue::FreeAllSurfaces() {
FreeSurface(¤t_surface_);
while (!in_flight_surfaces_.empty()) {
FreeSurface(&in_flight_surfaces_.front());
in_flight_surfaces_.pop_front();
}
for (size_t i = 0; i < available_surfaces_.size(); i++)
FreeSurface(&available_surfaces_[i]);
available_surfaces_.clear();
}
void BufferQueue::FreeSurface(AllocatedSurface* surface) {
if (surface->texture) {
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
gl->BindTexture(GL_TEXTURE_2D, surface->texture);
gl->ReleaseTexImage2DCHROMIUM(GL_TEXTURE_2D, surface->image);
gl->DeleteTextures(1, &surface->texture);
gl->DestroyImageCHROMIUM(surface->image);
surface->image = 0;
surface->texture = 0;
allocated_count_--;
}
}
BufferQueue::AllocatedSurface BufferQueue::GetNextSurface() {
if (!available_surfaces_.empty()) {
AllocatedSurface surface = available_surfaces_.back();
available_surfaces_.pop_back();
return surface;
}
unsigned int texture = 0;
gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
gl->GenTextures(1, &texture);
if (!texture)
return AllocatedSurface();
// We don't want to allow anything more than triple buffering.
DCHECK_LT(allocated_count_, 4U);
unsigned int id = gl->CreateImageCHROMIUM(
size_.width(),
size_.height(),
internalformat_,
GL_IMAGE_SCANOUT_CHROMIUM);
if (!id) {
LOG(ERROR) << "Failed to allocate backing CreateImageCHROMIUM surface";
gl->DeleteTextures(1, &texture);
return AllocatedSurface();
}
allocated_count_++;
gl->BindTexture(GL_TEXTURE_2D, texture);
gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, id);
return AllocatedSurface(texture, id, gfx::Rect(size_));
}
} // namespace content