// 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/strings/stringprintf.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/command_buffer/service/renderbuffer_manager.h" #include "gpu/command_buffer/service/texture_manager.h" #include "ui/gl/gl_bindings.h" namespace gpu { namespace gles2 { DecoderFramebufferState::DecoderFramebufferState() : clear_state_dirty(false), bound_read_framebuffer(NULL), bound_draw_framebuffer(NULL) { } DecoderFramebufferState::~DecoderFramebufferState() { } Framebuffer::FramebufferComboCompleteMap* Framebuffer::framebuffer_combo_complete_map_; // Framebuffer completeness is not cacheable on OS X because of dynamic // graphics switching. // http://crbug.com/180876 #if defined(OS_MACOSX) bool Framebuffer::allow_framebuffer_combo_complete_map_ = false; #else bool Framebuffer::allow_framebuffer_combo_complete_map_ = true; #endif void Framebuffer::ClearFramebufferCompleteComboMap() { if (framebuffer_combo_complete_map_) { framebuffer_combo_complete_map_->clear(); } } class RenderbufferAttachment : public Framebuffer::Attachment { public: explicit RenderbufferAttachment( Renderbuffer* 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 GLenum texture_type() const OVERRIDE { return 0; } virtual GLsizei samples() const OVERRIDE { return renderbuffer_->samples(); } virtual GLuint object_name() const OVERRIDE { return renderbuffer_->client_id(); } virtual bool cleared() const OVERRIDE { return renderbuffer_->cleared(); } virtual void SetCleared( RenderbufferManager* renderbuffer_manager, TextureManager* /* texture_manager */, bool cleared) OVERRIDE { renderbuffer_manager->SetCleared(renderbuffer_.get(), cleared); } virtual bool IsTexture( TextureRef* /* texture */) const OVERRIDE { return false; } virtual bool IsRenderbuffer( Renderbuffer* renderbuffer) const OVERRIDE { return renderbuffer_.get() == renderbuffer; } virtual bool CanRenderTo() const OVERRIDE { return true; } virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const OVERRIDE { // Nothing to do for renderbuffers. } virtual bool ValidForAttachmentType( GLenum attachment_type, uint32 max_color_attachments) OVERRIDE { uint32 need = GLES2Util::GetChannelsNeededForAttachmentType( attachment_type, max_color_attachments); uint32 have = GLES2Util::GetChannelsForFormat(internal_format()); return (need & have) != 0; } Renderbuffer* renderbuffer() const { return renderbuffer_.get(); } virtual void AddToSignature( TextureManager* texture_manager, std::string* signature) const OVERRIDE { DCHECK(signature); renderbuffer_->AddToSignature(signature); } virtual void OnWillRenderTo() const OVERRIDE {} virtual void OnDidRenderTo() const OVERRIDE {} protected: virtual ~RenderbufferAttachment() { } private: scoped_refptr renderbuffer_; DISALLOW_COPY_AND_ASSIGN(RenderbufferAttachment); }; class TextureAttachment : public Framebuffer::Attachment { public: TextureAttachment( TextureRef* texture_ref, GLenum target, GLint level, GLsizei samples) : texture_ref_(texture_ref), target_(target), level_(level), samples_(samples) { } virtual GLsizei width() const OVERRIDE { GLsizei temp_width = 0; GLsizei temp_height = 0; texture_ref_->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_ref_->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_ref_->texture()->GetLevelType( target_, level_, &temp_type, &temp_internal_format); return temp_internal_format; } virtual GLenum texture_type() const OVERRIDE { GLenum temp_type = 0; GLenum temp_internal_format = 0; texture_ref_->texture()->GetLevelType( target_, level_, &temp_type, &temp_internal_format); return temp_type; } virtual GLsizei samples() const OVERRIDE { return samples_; } virtual GLuint object_name() const OVERRIDE { return texture_ref_->client_id(); } virtual bool cleared() const OVERRIDE { return texture_ref_->texture()->IsLevelCleared(target_, level_); } virtual void SetCleared( RenderbufferManager* /* renderbuffer_manager */, TextureManager* texture_manager, bool cleared) OVERRIDE { texture_manager->SetLevelCleared( texture_ref_.get(), target_, level_, cleared); } virtual bool IsTexture(TextureRef* texture) const OVERRIDE { return texture == texture_ref_.get(); } virtual bool IsRenderbuffer( Renderbuffer* /* renderbuffer */) const OVERRIDE { return false; } TextureRef* texture() const { return texture_ref_.get(); } virtual bool CanRenderTo() const OVERRIDE { return texture_ref_->texture()->CanRenderTo(); } virtual void DetachFromFramebuffer(Framebuffer* framebuffer) const OVERRIDE { texture_ref_->texture()->DetachFromFramebuffer(); framebuffer->OnTextureRefDetached(texture_ref_.get()); } virtual bool ValidForAttachmentType( GLenum attachment_type, uint32 max_color_attachments) OVERRIDE { GLenum type = 0; GLenum internal_format = 0; if (!texture_ref_->texture()->GetLevelType( target_, level_, &type, &internal_format)) { return false; } uint32 need = GLES2Util::GetChannelsNeededForAttachmentType( attachment_type, max_color_attachments); uint32 have = GLES2Util::GetChannelsForFormat(internal_format); // Workaround for NVIDIA drivers that incorrectly expose these formats as // renderable: if (internal_format == GL_LUMINANCE || internal_format == GL_ALPHA || internal_format == GL_LUMINANCE_ALPHA) { return false; } return (need & have) != 0; } virtual void AddToSignature( TextureManager* texture_manager, std::string* signature) const OVERRIDE { DCHECK(signature); texture_manager->AddToSignature( texture_ref_.get(), target_, level_, signature); } virtual void OnWillRenderTo() const OVERRIDE { texture_ref_->texture()->OnWillModifyPixels(); } virtual void OnDidRenderTo() const OVERRIDE { texture_ref_->texture()->OnDidModifyPixels(); } protected: virtual ~TextureAttachment() {} private: scoped_refptr texture_ref_; GLenum target_; GLint level_; GLsizei samples_; DISALLOW_COPY_AND_ASSIGN(TextureAttachment); }; FramebufferManager::TextureDetachObserver::TextureDetachObserver() {} FramebufferManager::TextureDetachObserver::~TextureDetachObserver() {} FramebufferManager::FramebufferManager( uint32 max_draw_buffers, uint32 max_color_attachments) : framebuffer_state_change_count_(1), framebuffer_count_(0), have_context_(true), max_draw_buffers_(max_draw_buffers), max_color_attachments_(max_color_attachments) { DCHECK_GT(max_draw_buffers_, 0u); DCHECK_GT(max_color_attachments_, 0u); } FramebufferManager::~FramebufferManager() { DCHECK(framebuffers_.empty()); // If this triggers, that means something is keeping a reference to a // Framebuffer belonging to this. CHECK_EQ(framebuffer_count_, 0u); } void Framebuffer::MarkAsDeleted() { deleted_ = true; while (!attachments_.empty()) { Attachment* attachment = attachments_.begin()->second.get(); attachment->DetachFromFramebuffer(this); attachments_.erase(attachments_.begin()); } } void FramebufferManager::Destroy(bool have_context) { have_context_ = have_context; framebuffers_.clear(); } void FramebufferManager::StartTracking( Framebuffer* /* framebuffer */) { ++framebuffer_count_; } void FramebufferManager::StopTracking( Framebuffer* /* framebuffer */) { --framebuffer_count_; } void FramebufferManager::CreateFramebuffer( GLuint client_id, GLuint service_id) { std::pair result = framebuffers_.insert( std::make_pair( client_id, scoped_refptr( new Framebuffer(this, service_id)))); DCHECK(result.second); } Framebuffer::Framebuffer( 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); DCHECK_GT(manager->max_draw_buffers_, 0u); draw_buffers_.reset(new GLenum[manager->max_draw_buffers_]); draw_buffers_[0] = GL_COLOR_ATTACHMENT0; for (uint32 i = 1; i < manager->max_draw_buffers_; ++i) draw_buffers_[i] = GL_NONE; } Framebuffer::~Framebuffer() { if (manager_) { if (manager_->have_context_) { GLuint id = service_id(); glDeleteFramebuffersEXT(1, &id); } manager_->StopTracking(this); manager_ = NULL; } } bool Framebuffer::HasUnclearedAttachment( GLenum attachment) const { AttachmentMap::const_iterator it = attachments_.find(attachment); if (it != attachments_.end()) { const Attachment* attachment = it->second.get(); return !attachment->cleared(); } return false; } void Framebuffer::MarkAttachmentAsCleared( RenderbufferManager* renderbuffer_manager, TextureManager* texture_manager, GLenum attachment, bool cleared) { AttachmentMap::iterator it = attachments_.find(attachment); if (it != attachments_.end()) { Attachment* a = it->second.get(); if (a->cleared() != cleared) { a->SetCleared(renderbuffer_manager, texture_manager, cleared); } } } void Framebuffer::MarkAttachmentsAsCleared( RenderbufferManager* renderbuffer_manager, TextureManager* texture_manager, bool cleared) { for (AttachmentMap::iterator it = attachments_.begin(); it != attachments_.end(); ++it) { Attachment* attachment = it->second.get(); if (attachment->cleared() != cleared) { attachment->SetCleared(renderbuffer_manager, texture_manager, cleared); } } } bool Framebuffer::HasDepthAttachment() const { return attachments_.find(GL_DEPTH_STENCIL_ATTACHMENT) != attachments_.end() || attachments_.find(GL_DEPTH_ATTACHMENT) != attachments_.end(); } bool Framebuffer::HasStencilAttachment() const { return attachments_.find(GL_DEPTH_STENCIL_ATTACHMENT) != attachments_.end() || attachments_.find(GL_STENCIL_ATTACHMENT) != attachments_.end(); } GLenum Framebuffer::GetColorAttachmentFormat() const { AttachmentMap::const_iterator it = attachments_.find(GL_COLOR_ATTACHMENT0); if (it == attachments_.end()) { return 0; } const Attachment* attachment = it->second.get(); return attachment->internal_format(); } GLenum Framebuffer::GetColorAttachmentTextureType() const { AttachmentMap::const_iterator it = attachments_.find(GL_COLOR_ATTACHMENT0); if (it == attachments_.end()) { return 0; } const Attachment* attachment = it->second.get(); return attachment->texture_type(); } GLenum Framebuffer::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.get(); if (!attachment->ValidForAttachmentType(attachment_type, manager_->max_color_attachments_)) { 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 Framebuffer::GetStatus( TextureManager* texture_manager, GLenum target) const { // Check if we have this combo already. std::string signature; if (allow_framebuffer_combo_complete_map_) { signature = base::StringPrintf("|FBO|target=%04x", target); for (AttachmentMap::const_iterator it = attachments_.begin(); it != attachments_.end(); ++it) { Attachment* attachment = it->second.get(); 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); // Insert the new result into the combo map. if (allow_framebuffer_combo_complete_map_ && result == GL_FRAMEBUFFER_COMPLETE) { framebuffer_combo_complete_map_->insert(std::make_pair(signature, true)); } return result; } bool Framebuffer::IsCleared() const { // are all the attachments cleaared? for (AttachmentMap::const_iterator it = attachments_.begin(); it != attachments_.end(); ++it) { Attachment* attachment = it->second.get(); if (!attachment->cleared()) { return false; } } return true; } GLenum Framebuffer::GetDrawBuffer(GLenum draw_buffer) const { GLsizei index = static_cast( draw_buffer - GL_DRAW_BUFFER0_ARB); CHECK(index >= 0 && index < static_cast(manager_->max_draw_buffers_)); return draw_buffers_[index]; } void Framebuffer::SetDrawBuffers(GLsizei n, const GLenum* bufs) { DCHECK(n <= static_cast(manager_->max_draw_buffers_)); for (GLsizei i = 0; i < n; ++i) draw_buffers_[i] = bufs[i]; } bool Framebuffer::HasAlphaMRT() const { for (uint32 i = 0; i < manager_->max_draw_buffers_; ++i) { if (draw_buffers_[i] != GL_NONE) { const Attachment* attachment = GetAttachment(draw_buffers_[i]); if (!attachment) continue; if ((GLES2Util::GetChannelsForFormat( attachment->internal_format()) & 0x0008) != 0) return true; } } return false; } void Framebuffer::UnbindRenderbuffer( GLenum target, Renderbuffer* renderbuffer) { bool done; do { done = true; for (AttachmentMap::const_iterator it = attachments_.begin(); it != attachments_.end(); ++it) { Attachment* attachment = it->second.get(); 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 Framebuffer::UnbindTexture( GLenum target, TextureRef* texture_ref) { bool done; do { done = true; for (AttachmentMap::const_iterator it = attachments_.begin(); it != attachments_.end(); ++it) { Attachment* attachment = it->second.get(); if (attachment->IsTexture(texture_ref)) { // TODO(gman): manually detach texture. // glFramebufferTexture2DEXT(target, it->first, GL_TEXTURE_2D, 0, 0); AttachTexture(it->first, NULL, GL_TEXTURE_2D, 0, 0); done = false; break; } } } while (!done); } Framebuffer* FramebufferManager::GetFramebuffer( GLuint client_id) { FramebufferMap::iterator it = framebuffers_.find(client_id); return it != framebuffers_.end() ? it->second.get() : NULL; } void FramebufferManager::RemoveFramebuffer(GLuint client_id) { FramebufferMap::iterator it = framebuffers_.find(client_id); if (it != framebuffers_.end()) { it->second->MarkAsDeleted(); framebuffers_.erase(it); } } void Framebuffer::AttachRenderbuffer( GLenum attachment, Renderbuffer* renderbuffer) { const Attachment* a = GetAttachment(attachment); if (a) a->DetachFromFramebuffer(this); if (renderbuffer) { attachments_[attachment] = scoped_refptr( new RenderbufferAttachment(renderbuffer)); } else { attachments_.erase(attachment); } framebuffer_complete_state_count_id_ = 0; } void Framebuffer::AttachTexture( GLenum attachment, TextureRef* texture_ref, GLenum target, GLint level, GLsizei samples) { const Attachment* a = GetAttachment(attachment); if (a) a->DetachFromFramebuffer(this); if (texture_ref) { attachments_[attachment] = scoped_refptr( new TextureAttachment(texture_ref, target, level, samples)); texture_ref->texture()->AttachToFramebuffer(); } else { attachments_.erase(attachment); } framebuffer_complete_state_count_id_ = 0; } const Framebuffer::Attachment* Framebuffer::GetAttachment( GLenum attachment) const { AttachmentMap::const_iterator it = attachments_.find(attachment); if (it != attachments_.end()) { return it->second.get(); } return NULL; } void Framebuffer::OnTextureRefDetached(TextureRef* texture) { manager_->OnTextureRefDetached(texture); } void Framebuffer::OnWillRenderTo() const { for (AttachmentMap::const_iterator it = attachments_.begin(); it != attachments_.end(); ++it) { it->second->OnWillRenderTo(); } } void Framebuffer::OnDidRenderTo() const { for (AttachmentMap::const_iterator it = attachments_.begin(); it != attachments_.end(); ++it) { it->second->OnDidRenderTo(); } } 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 (FramebufferMap::const_iterator it = framebuffers_.begin(); it != framebuffers_.end(); ++it) { if (it->second->service_id() == service_id) { *client_id = it->first; return true; } } return false; } void FramebufferManager::MarkAttachmentsAsCleared( Framebuffer* framebuffer, RenderbufferManager* renderbuffer_manager, TextureManager* texture_manager) { DCHECK(framebuffer); framebuffer->MarkAttachmentsAsCleared(renderbuffer_manager, texture_manager, true); MarkAsComplete(framebuffer); } void FramebufferManager::MarkAsComplete( Framebuffer* framebuffer) { DCHECK(framebuffer); framebuffer->MarkAsComplete(framebuffer_state_change_count_); } bool FramebufferManager::IsComplete( Framebuffer* framebuffer) { DCHECK(framebuffer); return framebuffer->framebuffer_complete_state_count_id() == framebuffer_state_change_count_; } void FramebufferManager::OnTextureRefDetached(TextureRef* texture) { for (TextureDetachObserverVector::iterator it = texture_detach_observers_.begin(); it != texture_detach_observers_.end(); ++it) { TextureDetachObserver* observer = *it; observer->OnTextureRefDetachedFromFramebuffer(texture); } } } // namespace gles2 } // namespace gpu