blob: 0d73f966a52b4cc200954fe2527d355f8436adf5 [file] [log] [blame]
// 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 "gpu/command_buffer/service/framebuffer_manager.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "ui/gl/gl_bindings.h"
namespace gpu {
namespace gles2 {
FramebufferManager::FramebufferInfo::FramebufferComboCompleteMap*
FramebufferManager::FramebufferInfo::framebuffer_combo_complete_map_;
void FramebufferManager::FramebufferInfo::ClearFramebufferCompleteComboMap() {
if (framebuffer_combo_complete_map_) {
framebuffer_combo_complete_map_->clear();
}
}
class RenderbufferAttachment
: public FramebufferManager::FramebufferInfo::Attachment {
public:
explicit RenderbufferAttachment(
RenderbufferManager::RenderbufferInfo* renderbuffer)
: renderbuffer_(renderbuffer) {
}
virtual GLsizei width() const OVERRIDE {
return renderbuffer_->width();
}
virtual GLsizei height() const OVERRIDE {
return renderbuffer_->height();
}
virtual GLenum internal_format() const OVERRIDE {
return renderbuffer_->internal_format();
}
virtual GLsizei samples() const OVERRIDE {
return renderbuffer_->samples();
}
virtual bool cleared() const OVERRIDE {
return renderbuffer_->cleared();
}
virtual void SetCleared(
RenderbufferManager* renderbuffer_manager,
TextureManager* /* texture_manager */) OVERRIDE {
renderbuffer_manager->SetCleared(renderbuffer_);
}
virtual bool IsTexture(
TextureManager::TextureInfo* /* texture */) const OVERRIDE {
return false;
}
virtual bool IsRenderbuffer(
RenderbufferManager::RenderbufferInfo* renderbuffer) const OVERRIDE {
return renderbuffer_ == renderbuffer;
}
virtual bool CanRenderTo() const OVERRIDE {
return true;
}
virtual void DetachFromFramebuffer() OVERRIDE {
// Nothing to do for renderbuffers.
}
virtual bool ValidForAttachmentType(GLenum attachment_type) OVERRIDE {
uint32 need = GLES2Util::GetChannelsNeededForAttachmentType(
attachment_type);
uint32 have = GLES2Util::GetChannelsForFormat(internal_format());
return (need & have) != 0;
}
RenderbufferManager::RenderbufferInfo* renderbuffer() const {
return renderbuffer_.get();
}
virtual void AddToSignature(
TextureManager* texture_manager, std::string* signature) const OVERRIDE {
DCHECK(signature);
renderbuffer_->AddToSignature(signature);
}
protected:
virtual ~RenderbufferAttachment() { }
private:
RenderbufferManager::RenderbufferInfo::Ref renderbuffer_;
DISALLOW_COPY_AND_ASSIGN(RenderbufferAttachment);
};
class TextureAttachment
: public FramebufferManager::FramebufferInfo::Attachment {
public:
TextureAttachment(
TextureManager::TextureInfo* texture, GLenum target, GLint level)
: texture_(texture),
target_(target),
level_(level) {
}
virtual GLsizei width() const OVERRIDE {
GLsizei temp_width = 0;
GLsizei temp_height = 0;
texture_->GetLevelSize(target_, level_, &temp_width, &temp_height);
return temp_width;
}
virtual GLsizei height() const OVERRIDE {
GLsizei temp_width = 0;
GLsizei temp_height = 0;
texture_->GetLevelSize(target_, level_, &temp_width, &temp_height);
return temp_height;
}
virtual GLenum internal_format() const OVERRIDE {
GLenum temp_type = 0;
GLenum temp_internal_format = 0;
texture_->GetLevelType(target_, level_, &temp_type, &temp_internal_format);
return temp_internal_format;
}
virtual GLsizei samples() const OVERRIDE {
return 0;
}
virtual bool cleared() const OVERRIDE {
return texture_->IsLevelCleared(target_, level_);
}
virtual void SetCleared(
RenderbufferManager* /* renderbuffer_manager */,
TextureManager* texture_manager) OVERRIDE {
texture_manager->SetLevelCleared(texture_, target_, level_);
}
virtual bool IsTexture(TextureManager::TextureInfo* texture) const OVERRIDE {
return texture == texture_.get();
}
virtual bool IsRenderbuffer(
RenderbufferManager::RenderbufferInfo* /* renderbuffer */)
const OVERRIDE {
return false;
}
TextureManager::TextureInfo* texture() const {
return texture_.get();
}
virtual bool CanRenderTo() const OVERRIDE {
return texture_->CanRenderTo();
}
virtual void DetachFromFramebuffer() OVERRIDE {
texture_->DetachFromFramebuffer();
}
virtual bool ValidForAttachmentType(GLenum attachment_type) OVERRIDE {
GLenum type = 0;
GLenum internal_format = 0;
if (!texture_->GetLevelType(target_, level_, &type, &internal_format)) {
return false;
}
uint32 need = GLES2Util::GetChannelsNeededForAttachmentType(
attachment_type);
uint32 have = GLES2Util::GetChannelsForFormat(internal_format);
return (need & have) != 0;
}
virtual void AddToSignature(
TextureManager* texture_manager, std::string* signature) const OVERRIDE {
DCHECK(signature);
texture_manager->AddToSignature(texture_, target_, level_, signature);
}
protected:
virtual ~TextureAttachment() {}
private:
TextureManager::TextureInfo::Ref texture_;
GLenum target_;
GLint level_;
DISALLOW_COPY_AND_ASSIGN(TextureAttachment);
};
FramebufferManager::FramebufferManager()
: framebuffer_state_change_count_(1),
framebuffer_info_count_(0),
have_context_(true) {
}
FramebufferManager::~FramebufferManager() {
DCHECK(framebuffer_infos_.empty());
// If this triggers, that means something is keeping a reference to a
// FramebufferInfo belonging to this.
CHECK_EQ(framebuffer_info_count_, 0u);
}
void FramebufferManager::FramebufferInfo::MarkAsDeleted() {
deleted_ = true;
while (!attachments_.empty()) {
Attachment* attachment = attachments_.begin()->second.get();
attachment->DetachFromFramebuffer();
attachments_.erase(attachments_.begin());
}
}
void FramebufferManager::Destroy(bool have_context) {
have_context_ = have_context;
framebuffer_infos_.clear();
}
void FramebufferManager::StartTracking(
FramebufferManager::FramebufferInfo* /* framebuffer */) {
++framebuffer_info_count_;
}
void FramebufferManager::StopTracking(
FramebufferManager::FramebufferInfo* /* framebuffer */) {
--framebuffer_info_count_;
}
void FramebufferManager::CreateFramebufferInfo(
GLuint client_id, GLuint service_id) {
std::pair<FramebufferInfoMap::iterator, bool> result =
framebuffer_infos_.insert(
std::make_pair(
client_id,
FramebufferInfo::Ref(new FramebufferInfo(this, service_id))));
DCHECK(result.second);
}
FramebufferManager::FramebufferInfo::FramebufferInfo(
FramebufferManager* manager, GLuint service_id)
: manager_(manager),
deleted_(false),
service_id_(service_id),
has_been_bound_(false),
framebuffer_complete_state_count_id_(0) {
manager->StartTracking(this);
}
FramebufferManager::FramebufferInfo::~FramebufferInfo() {
if (manager_) {
if (manager_->have_context_) {
GLuint id = service_id();
glDeleteFramebuffersEXT(1, &id);
}
manager_->StopTracking(this);
manager_ = NULL;
}
}
bool FramebufferManager::FramebufferInfo::HasUnclearedAttachment(
GLenum attachment) const {
AttachmentMap::const_iterator it =
attachments_.find(attachment);
if (it != attachments_.end()) {
const Attachment* attachment = it->second;
return !attachment->cleared();
}
return false;
}
void FramebufferManager::FramebufferInfo::MarkAttachmentsAsCleared(
RenderbufferManager* renderbuffer_manager,
TextureManager* texture_manager) {
for (AttachmentMap::iterator it = attachments_.begin();
it != attachments_.end(); ++it) {
Attachment* attachment = it->second;
if (!attachment->cleared()) {
attachment->SetCleared(renderbuffer_manager, texture_manager);
}
}
}
bool FramebufferManager::FramebufferInfo::HasDepthAttachment() const {
return attachments_.find(GL_DEPTH_STENCIL_ATTACHMENT) != attachments_.end() ||
attachments_.find(GL_DEPTH_ATTACHMENT) != attachments_.end();
}
bool FramebufferManager::FramebufferInfo::HasStencilAttachment() const {
return attachments_.find(GL_DEPTH_STENCIL_ATTACHMENT) != attachments_.end() ||
attachments_.find(GL_STENCIL_ATTACHMENT) != attachments_.end();
}
GLenum FramebufferManager::FramebufferInfo::GetColorAttachmentFormat() const {
AttachmentMap::const_iterator it = attachments_.find(GL_COLOR_ATTACHMENT0);
if (it == attachments_.end()) {
return 0;
}
const Attachment* attachment = it->second;
return attachment->internal_format();
}
GLenum FramebufferManager::FramebufferInfo::IsPossiblyComplete() const {
if (attachments_.empty()) {
return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
}
GLsizei width = -1;
GLsizei height = -1;
for (AttachmentMap::const_iterator it = attachments_.begin();
it != attachments_.end(); ++it) {
GLenum attachment_type = it->first;
Attachment* attachment = it->second;
if (!attachment->ValidForAttachmentType(attachment_type)) {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
if (width < 0) {
width = attachment->width();
height = attachment->height();
if (width == 0 || height == 0) {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
} else {
if (attachment->width() != width || attachment->height() != height) {
return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT;
}
}
if (!attachment->CanRenderTo()) {
return GL_FRAMEBUFFER_UNSUPPORTED;
}
}
// This does not mean the framebuffer is actually complete. It just means our
// checks passed.
return GL_FRAMEBUFFER_COMPLETE;
}
GLenum FramebufferManager::FramebufferInfo::GetStatus(
TextureManager* texture_manager, GLenum target) const {
// Check if we have this combo already.
std::string signature(base::StringPrintf("|FBO|target=%04x", target));
for (AttachmentMap::const_iterator it = attachments_.begin();
it != attachments_.end(); ++it) {
Attachment* attachment = it->second;
signature += base::StringPrintf(
"|Attachment|attachmentpoint=%04x", it->first);
attachment->AddToSignature(texture_manager, &signature);
}
if (!framebuffer_combo_complete_map_) {
framebuffer_combo_complete_map_ = new FramebufferComboCompleteMap();
}
FramebufferComboCompleteMap::const_iterator it =
framebuffer_combo_complete_map_->find(signature);
if (it != framebuffer_combo_complete_map_->end()) {
return GL_FRAMEBUFFER_COMPLETE;
}
GLenum result = glCheckFramebufferStatusEXT(target);
if (result == GL_FRAMEBUFFER_COMPLETE) {
framebuffer_combo_complete_map_->insert(std::make_pair(signature, true));
}
return result;
}
bool FramebufferManager::FramebufferInfo::IsCleared() const {
// are all the attachments cleaared?
for (AttachmentMap::const_iterator it = attachments_.begin();
it != attachments_.end(); ++it) {
Attachment* attachment = it->second;
if (!attachment->cleared()) {
return false;
}
}
return true;
}
void FramebufferManager::FramebufferInfo::UnbindRenderbuffer(
GLenum target, RenderbufferManager::RenderbufferInfo* renderbuffer) {
bool done;
do {
done = true;
for (AttachmentMap::const_iterator it = attachments_.begin();
it != attachments_.end(); ++it) {
Attachment* attachment = it->second;
if (attachment->IsRenderbuffer(renderbuffer)) {
// TODO(gman): manually detach renderbuffer.
// glFramebufferRenderbufferEXT(target, it->first, GL_RENDERBUFFER, 0);
AttachRenderbuffer(it->first, NULL);
done = false;
break;
}
}
} while (!done);
}
void FramebufferManager::FramebufferInfo::UnbindTexture(
GLenum target, TextureManager::TextureInfo* texture) {
bool done;
do {
done = true;
for (AttachmentMap::const_iterator it = attachments_.begin();
it != attachments_.end(); ++it) {
Attachment* attachment = it->second;
if (attachment->IsTexture(texture)) {
// TODO(gman): manually detach texture.
// glFramebufferTexture2DEXT(target, it->first, GL_TEXTURE_2D, 0, 0);
AttachTexture(it->first, NULL, GL_TEXTURE_2D, 0);
done = false;
break;
}
}
} while (!done);
}
FramebufferManager::FramebufferInfo* FramebufferManager::GetFramebufferInfo(
GLuint client_id) {
FramebufferInfoMap::iterator it = framebuffer_infos_.find(client_id);
return it != framebuffer_infos_.end() ? it->second : NULL;
}
void FramebufferManager::RemoveFramebufferInfo(GLuint client_id) {
FramebufferInfoMap::iterator it = framebuffer_infos_.find(client_id);
if (it != framebuffer_infos_.end()) {
it->second->MarkAsDeleted();
framebuffer_infos_.erase(it);
}
}
void FramebufferManager::FramebufferInfo::AttachRenderbuffer(
GLenum attachment, RenderbufferManager::RenderbufferInfo* renderbuffer) {
DCHECK(attachment == GL_COLOR_ATTACHMENT0 ||
attachment == GL_DEPTH_ATTACHMENT ||
attachment == GL_STENCIL_ATTACHMENT ||
attachment == GL_DEPTH_STENCIL_ATTACHMENT);
if (renderbuffer) {
attachments_[attachment] = Attachment::Ref(
new RenderbufferAttachment(renderbuffer));
} else {
attachments_.erase(attachment);
}
framebuffer_complete_state_count_id_ = 0;
}
void FramebufferManager::FramebufferInfo::AttachTexture(
GLenum attachment, TextureManager::TextureInfo* texture, GLenum target,
GLint level) {
DCHECK(attachment == GL_COLOR_ATTACHMENT0 ||
attachment == GL_DEPTH_ATTACHMENT ||
attachment == GL_STENCIL_ATTACHMENT ||
attachment == GL_DEPTH_STENCIL_ATTACHMENT);
const Attachment* a = GetAttachment(attachment);
if (a && a->IsTexture(texture)) {
texture->DetachFromFramebuffer();
}
if (texture) {
attachments_[attachment] = Attachment::Ref(
new TextureAttachment(texture, target, level));
texture->AttachToFramebuffer();
} else {
attachments_.erase(attachment);
}
framebuffer_complete_state_count_id_ = 0;
}
const FramebufferManager::FramebufferInfo::Attachment*
FramebufferManager::FramebufferInfo::GetAttachment(
GLenum attachment) const {
AttachmentMap::const_iterator it = attachments_.find(attachment);
if (it != attachments_.end()) {
return it->second;
}
return NULL;
}
bool FramebufferManager::GetClientId(
GLuint service_id, GLuint* client_id) const {
// This doesn't need to be fast. It's only used during slow queries.
for (FramebufferInfoMap::const_iterator it = framebuffer_infos_.begin();
it != framebuffer_infos_.end(); ++it) {
if (it->second->service_id() == service_id) {
*client_id = it->first;
return true;
}
}
return false;
}
void FramebufferManager::MarkAttachmentsAsCleared(
FramebufferManager::FramebufferInfo* framebuffer,
RenderbufferManager* renderbuffer_manager,
TextureManager* texture_manager) {
DCHECK(framebuffer);
framebuffer->MarkAttachmentsAsCleared(renderbuffer_manager, texture_manager);
MarkAsComplete(framebuffer);
}
void FramebufferManager::MarkAsComplete(
FramebufferManager::FramebufferInfo* framebuffer) {
DCHECK(framebuffer);
framebuffer->MarkAsComplete(framebuffer_state_change_count_);
}
bool FramebufferManager::IsComplete(
FramebufferManager::FramebufferInfo* framebuffer) {
DCHECK(framebuffer);
return framebuffer->framebuffer_complete_state_count_id() ==
framebuffer_state_change_count_;
}
} // namespace gles2
} // namespace gpu