summaryrefslogtreecommitdiff
path: root/chromium/gpu/command_buffer/service/framebuffer_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/gpu/command_buffer/service/framebuffer_manager.cc')
-rw-r--r--chromium/gpu/command_buffer/service/framebuffer_manager.cc618
1 files changed, 618 insertions, 0 deletions
diff --git a/chromium/gpu/command_buffer/service/framebuffer_manager.cc b/chromium/gpu/command_buffer/service/framebuffer_manager.cc
new file mode 100644
index 00000000000..b4c6b090f0f
--- /dev/null
+++ b/chromium/gpu/command_buffer/service/framebuffer_manager.cc
@@ -0,0 +1,618 @@
+// 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 {
+
+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 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() 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);
+ }
+
+ protected:
+ virtual ~RenderbufferAttachment() { }
+
+ private:
+ scoped_refptr<Renderbuffer> 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 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() const OVERRIDE {
+ texture_ref_->texture()->DetachFromFramebuffer();
+ }
+
+ 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);
+ }
+
+ protected:
+ virtual ~TextureAttachment() {}
+
+ private:
+ scoped_refptr<TextureRef> texture_ref_;
+ GLenum target_;
+ GLint level_;
+ GLsizei samples_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextureAttachment);
+};
+
+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();
+ 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<FramebufferMap::iterator, bool> result =
+ framebuffers_.insert(
+ std::make_pair(
+ client_id,
+ scoped_refptr<Framebuffer>(
+ 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::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<GLsizei>(
+ draw_buffer - GL_DRAW_BUFFER0_ARB);
+ CHECK(index >= 0 &&
+ index < static_cast<GLsizei>(manager_->max_draw_buffers_));
+ return draw_buffers_[index];
+}
+
+void Framebuffer::SetDrawBuffers(GLsizei n, const GLenum* bufs) {
+ DCHECK(n <= static_cast<GLsizei>(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();
+ if (renderbuffer) {
+ attachments_[attachment] = scoped_refptr<Attachment>(
+ 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();
+ if (texture_ref) {
+ attachments_[attachment] = scoped_refptr<Attachment>(
+ 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;
+}
+
+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_;
+}
+
+} // namespace gles2
+} // namespace gpu
+
+