1 // Copyright (c) 2012 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.
5 #include "gpu/command_buffer/service/framebuffer_manager.h"
6 #include "base/logging.h"
7 #include "base/strings/stringprintf.h"
8 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
9 #include "gpu/command_buffer/service/renderbuffer_manager.h"
10 #include "gpu/command_buffer/service/texture_manager.h"
11 #include "ui/gl/gl_bindings.h"
16 DecoderFramebufferState::DecoderFramebufferState()
17 : clear_state_dirty(false),
18 bound_read_framebuffer(NULL
),
19 bound_draw_framebuffer(NULL
) {
22 DecoderFramebufferState::~DecoderFramebufferState() {
25 Framebuffer::FramebufferComboCompleteMap
*
26 Framebuffer::framebuffer_combo_complete_map_
;
28 // Framebuffer completeness is not cacheable on OS X because of dynamic
29 // graphics switching.
30 // http://crbug.com/180876
31 #if defined(OS_MACOSX)
32 bool Framebuffer::allow_framebuffer_combo_complete_map_
= false;
34 bool Framebuffer::allow_framebuffer_combo_complete_map_
= true;
37 void Framebuffer::ClearFramebufferCompleteComboMap() {
38 if (framebuffer_combo_complete_map_
) {
39 framebuffer_combo_complete_map_
->clear();
43 class RenderbufferAttachment
44 : public Framebuffer::Attachment
{
46 explicit RenderbufferAttachment(
47 Renderbuffer
* renderbuffer
)
48 : renderbuffer_(renderbuffer
) {
51 virtual GLsizei
width() const OVERRIDE
{
52 return renderbuffer_
->width();
55 virtual GLsizei
height() const OVERRIDE
{
56 return renderbuffer_
->height();
59 virtual GLenum
internal_format() const OVERRIDE
{
60 return renderbuffer_
->internal_format();
63 virtual GLenum
texture_type() const OVERRIDE
{
67 virtual GLsizei
samples() const OVERRIDE
{
68 return renderbuffer_
->samples();
71 virtual GLuint
object_name() const OVERRIDE
{
72 return renderbuffer_
->client_id();
75 virtual bool cleared() const OVERRIDE
{
76 return renderbuffer_
->cleared();
79 virtual void SetCleared(
80 RenderbufferManager
* renderbuffer_manager
,
81 TextureManager
* /* texture_manager */,
82 bool cleared
) OVERRIDE
{
83 renderbuffer_manager
->SetCleared(renderbuffer_
.get(), cleared
);
86 virtual bool IsTexture(
87 TextureRef
* /* texture */) const OVERRIDE
{
91 virtual bool IsRenderbuffer(
92 Renderbuffer
* renderbuffer
) const OVERRIDE
{
93 return renderbuffer_
.get() == renderbuffer
;
96 virtual bool CanRenderTo() const OVERRIDE
{
100 virtual void DetachFromFramebuffer(Framebuffer
* framebuffer
) const OVERRIDE
{
101 // Nothing to do for renderbuffers.
104 virtual bool ValidForAttachmentType(
105 GLenum attachment_type
, uint32 max_color_attachments
) OVERRIDE
{
106 uint32 need
= GLES2Util::GetChannelsNeededForAttachmentType(
107 attachment_type
, max_color_attachments
);
108 uint32 have
= GLES2Util::GetChannelsForFormat(internal_format());
109 return (need
& have
) != 0;
112 Renderbuffer
* renderbuffer() const {
113 return renderbuffer_
.get();
116 virtual void AddToSignature(
117 TextureManager
* texture_manager
, std::string
* signature
) const OVERRIDE
{
119 renderbuffer_
->AddToSignature(signature
);
122 virtual void OnWillRenderTo() const OVERRIDE
{}
123 virtual void OnDidRenderTo() const OVERRIDE
{}
126 virtual ~RenderbufferAttachment() { }
129 scoped_refptr
<Renderbuffer
> renderbuffer_
;
131 DISALLOW_COPY_AND_ASSIGN(RenderbufferAttachment
);
134 class TextureAttachment
135 : public Framebuffer::Attachment
{
138 TextureRef
* texture_ref
, GLenum target
, GLint level
, GLsizei samples
)
139 : texture_ref_(texture_ref
),
145 virtual GLsizei
width() const OVERRIDE
{
146 GLsizei temp_width
= 0;
147 GLsizei temp_height
= 0;
148 texture_ref_
->texture()->GetLevelSize(
149 target_
, level_
, &temp_width
, &temp_height
);
153 virtual GLsizei
height() const OVERRIDE
{
154 GLsizei temp_width
= 0;
155 GLsizei temp_height
= 0;
156 texture_ref_
->texture()->GetLevelSize(
157 target_
, level_
, &temp_width
, &temp_height
);
161 virtual GLenum
internal_format() const OVERRIDE
{
162 GLenum temp_type
= 0;
163 GLenum temp_internal_format
= 0;
164 texture_ref_
->texture()->GetLevelType(
165 target_
, level_
, &temp_type
, &temp_internal_format
);
166 return temp_internal_format
;
169 virtual GLenum
texture_type() const OVERRIDE
{
170 GLenum temp_type
= 0;
171 GLenum temp_internal_format
= 0;
172 texture_ref_
->texture()->GetLevelType(
173 target_
, level_
, &temp_type
, &temp_internal_format
);
177 virtual GLsizei
samples() const OVERRIDE
{
181 virtual GLuint
object_name() const OVERRIDE
{
182 return texture_ref_
->client_id();
185 virtual bool cleared() const OVERRIDE
{
186 return texture_ref_
->texture()->IsLevelCleared(target_
, level_
);
189 virtual void SetCleared(
190 RenderbufferManager
* /* renderbuffer_manager */,
191 TextureManager
* texture_manager
,
192 bool cleared
) OVERRIDE
{
193 texture_manager
->SetLevelCleared(
194 texture_ref_
.get(), target_
, level_
, cleared
);
197 virtual bool IsTexture(TextureRef
* texture
) const OVERRIDE
{
198 return texture
== texture_ref_
.get();
201 virtual bool IsRenderbuffer(
202 Renderbuffer
* /* renderbuffer */)
207 TextureRef
* texture() const {
208 return texture_ref_
.get();
211 virtual bool CanRenderTo() const OVERRIDE
{
212 return texture_ref_
->texture()->CanRenderTo();
215 virtual void DetachFromFramebuffer(Framebuffer
* framebuffer
)
217 texture_ref_
->texture()->DetachFromFramebuffer();
218 framebuffer
->OnTextureRefDetached(texture_ref_
.get());
221 virtual bool ValidForAttachmentType(
222 GLenum attachment_type
, uint32 max_color_attachments
) OVERRIDE
{
224 GLenum internal_format
= 0;
225 if (!texture_ref_
->texture()->GetLevelType(
226 target_
, level_
, &type
, &internal_format
)) {
229 uint32 need
= GLES2Util::GetChannelsNeededForAttachmentType(
230 attachment_type
, max_color_attachments
);
231 uint32 have
= GLES2Util::GetChannelsForFormat(internal_format
);
233 // Workaround for NVIDIA drivers that incorrectly expose these formats as
235 if (internal_format
== GL_LUMINANCE
|| internal_format
== GL_ALPHA
||
236 internal_format
== GL_LUMINANCE_ALPHA
) {
239 return (need
& have
) != 0;
242 virtual void AddToSignature(
243 TextureManager
* texture_manager
, std::string
* signature
) const OVERRIDE
{
245 texture_manager
->AddToSignature(
246 texture_ref_
.get(), target_
, level_
, signature
);
249 virtual void OnWillRenderTo() const OVERRIDE
{
250 texture_ref_
->texture()->OnWillModifyPixels();
253 virtual void OnDidRenderTo() const OVERRIDE
{
254 texture_ref_
->texture()->OnDidModifyPixels();
258 virtual ~TextureAttachment() {}
261 scoped_refptr
<TextureRef
> texture_ref_
;
266 DISALLOW_COPY_AND_ASSIGN(TextureAttachment
);
269 FramebufferManager::TextureDetachObserver::TextureDetachObserver() {}
271 FramebufferManager::TextureDetachObserver::~TextureDetachObserver() {}
273 FramebufferManager::FramebufferManager(
274 uint32 max_draw_buffers
, uint32 max_color_attachments
)
275 : framebuffer_state_change_count_(1),
276 framebuffer_count_(0),
278 max_draw_buffers_(max_draw_buffers
),
279 max_color_attachments_(max_color_attachments
) {
280 DCHECK_GT(max_draw_buffers_
, 0u);
281 DCHECK_GT(max_color_attachments_
, 0u);
284 FramebufferManager::~FramebufferManager() {
285 DCHECK(framebuffers_
.empty());
286 // If this triggers, that means something is keeping a reference to a
287 // Framebuffer belonging to this.
288 CHECK_EQ(framebuffer_count_
, 0u);
291 void Framebuffer::MarkAsDeleted() {
293 while (!attachments_
.empty()) {
294 Attachment
* attachment
= attachments_
.begin()->second
.get();
295 attachment
->DetachFromFramebuffer(this);
296 attachments_
.erase(attachments_
.begin());
300 void FramebufferManager::Destroy(bool have_context
) {
301 have_context_
= have_context
;
302 framebuffers_
.clear();
305 void FramebufferManager::StartTracking(
306 Framebuffer
* /* framebuffer */) {
307 ++framebuffer_count_
;
310 void FramebufferManager::StopTracking(
311 Framebuffer
* /* framebuffer */) {
312 --framebuffer_count_
;
315 void FramebufferManager::CreateFramebuffer(
316 GLuint client_id
, GLuint service_id
) {
317 std::pair
<FramebufferMap::iterator
, bool> result
=
318 framebuffers_
.insert(
321 scoped_refptr
<Framebuffer
>(
322 new Framebuffer(this, service_id
))));
323 DCHECK(result
.second
);
326 Framebuffer::Framebuffer(
327 FramebufferManager
* manager
, GLuint service_id
)
330 service_id_(service_id
),
331 has_been_bound_(false),
332 framebuffer_complete_state_count_id_(0) {
333 manager
->StartTracking(this);
334 DCHECK_GT(manager
->max_draw_buffers_
, 0u);
335 draw_buffers_
.reset(new GLenum
[manager
->max_draw_buffers_
]);
336 draw_buffers_
[0] = GL_COLOR_ATTACHMENT0
;
337 for (uint32 i
= 1; i
< manager
->max_draw_buffers_
; ++i
)
338 draw_buffers_
[i
] = GL_NONE
;
341 Framebuffer::~Framebuffer() {
343 if (manager_
->have_context_
) {
344 GLuint id
= service_id();
345 glDeleteFramebuffersEXT(1, &id
);
347 manager_
->StopTracking(this);
352 bool Framebuffer::HasUnclearedAttachment(
353 GLenum attachment
) const {
354 AttachmentMap::const_iterator it
=
355 attachments_
.find(attachment
);
356 if (it
!= attachments_
.end()) {
357 const Attachment
* attachment
= it
->second
.get();
358 return !attachment
->cleared();
363 bool Framebuffer::HasUnclearedColorAttachments() const {
364 for (AttachmentMap::const_iterator it
= attachments_
.begin();
365 it
!= attachments_
.end(); ++it
) {
366 if (it
->first
>= GL_COLOR_ATTACHMENT0
&&
367 it
->first
< GL_COLOR_ATTACHMENT0
+ manager_
->max_draw_buffers_
) {
368 const Attachment
* attachment
= it
->second
.get();
369 if (!attachment
->cleared())
376 void Framebuffer::ChangeDrawBuffersHelper(bool recover
) const {
377 scoped_ptr
<GLenum
[]> buffers(new GLenum
[manager_
->max_draw_buffers_
]);
378 for (uint32 i
= 0; i
< manager_
->max_draw_buffers_
; ++i
)
379 buffers
[i
] = GL_NONE
;
380 for (AttachmentMap::const_iterator it
= attachments_
.begin();
381 it
!= attachments_
.end(); ++it
) {
382 if (it
->first
>= GL_COLOR_ATTACHMENT0
&&
383 it
->first
< GL_COLOR_ATTACHMENT0
+ manager_
->max_draw_buffers_
) {
384 buffers
[it
->first
- GL_COLOR_ATTACHMENT0
] = it
->first
;
387 bool different
= false;
388 for (uint32 i
= 0; i
< manager_
->max_draw_buffers_
; ++i
) {
389 if (buffers
[i
] != draw_buffers_
[i
]) {
396 glDrawBuffersARB(manager_
->max_draw_buffers_
, draw_buffers_
.get());
398 glDrawBuffersARB(manager_
->max_draw_buffers_
, buffers
.get());
402 void Framebuffer::PrepareDrawBuffersForClear() const {
403 bool recover
= false;
404 ChangeDrawBuffersHelper(recover
);
407 void Framebuffer::RestoreDrawBuffersAfterClear() const {
409 ChangeDrawBuffersHelper(recover
);
412 void Framebuffer::MarkAttachmentAsCleared(
413 RenderbufferManager
* renderbuffer_manager
,
414 TextureManager
* texture_manager
,
417 AttachmentMap::iterator it
= attachments_
.find(attachment
);
418 if (it
!= attachments_
.end()) {
419 Attachment
* a
= it
->second
.get();
420 if (a
->cleared() != cleared
) {
421 a
->SetCleared(renderbuffer_manager
,
428 void Framebuffer::MarkAttachmentsAsCleared(
429 RenderbufferManager
* renderbuffer_manager
,
430 TextureManager
* texture_manager
,
432 for (AttachmentMap::iterator it
= attachments_
.begin();
433 it
!= attachments_
.end(); ++it
) {
434 Attachment
* attachment
= it
->second
.get();
435 if (attachment
->cleared() != cleared
) {
436 attachment
->SetCleared(renderbuffer_manager
, texture_manager
, cleared
);
441 bool Framebuffer::HasDepthAttachment() const {
442 return attachments_
.find(GL_DEPTH_STENCIL_ATTACHMENT
) != attachments_
.end() ||
443 attachments_
.find(GL_DEPTH_ATTACHMENT
) != attachments_
.end();
446 bool Framebuffer::HasStencilAttachment() const {
447 return attachments_
.find(GL_DEPTH_STENCIL_ATTACHMENT
) != attachments_
.end() ||
448 attachments_
.find(GL_STENCIL_ATTACHMENT
) != attachments_
.end();
451 GLenum
Framebuffer::GetColorAttachmentFormat() const {
452 AttachmentMap::const_iterator it
= attachments_
.find(GL_COLOR_ATTACHMENT0
);
453 if (it
== attachments_
.end()) {
456 const Attachment
* attachment
= it
->second
.get();
457 return attachment
->internal_format();
460 GLenum
Framebuffer::GetColorAttachmentTextureType() const {
461 AttachmentMap::const_iterator it
= attachments_
.find(GL_COLOR_ATTACHMENT0
);
462 if (it
== attachments_
.end()) {
465 const Attachment
* attachment
= it
->second
.get();
466 return attachment
->texture_type();
469 GLenum
Framebuffer::IsPossiblyComplete() const {
470 if (attachments_
.empty()) {
471 return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
;
476 for (AttachmentMap::const_iterator it
= attachments_
.begin();
477 it
!= attachments_
.end(); ++it
) {
478 GLenum attachment_type
= it
->first
;
479 Attachment
* attachment
= it
->second
.get();
480 if (!attachment
->ValidForAttachmentType(attachment_type
,
481 manager_
->max_color_attachments_
)) {
482 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
;
485 width
= attachment
->width();
486 height
= attachment
->height();
487 if (width
== 0 || height
== 0) {
488 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
;
491 if (attachment
->width() != width
|| attachment
->height() != height
) {
492 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT
;
496 if (!attachment
->CanRenderTo()) {
497 return GL_FRAMEBUFFER_UNSUPPORTED
;
501 // This does not mean the framebuffer is actually complete. It just means our
503 return GL_FRAMEBUFFER_COMPLETE
;
506 GLenum
Framebuffer::GetStatus(
507 TextureManager
* texture_manager
, GLenum target
) const {
508 // Check if we have this combo already.
509 std::string signature
;
510 if (allow_framebuffer_combo_complete_map_
) {
511 signature
= base::StringPrintf("|FBO|target=%04x", target
);
512 for (AttachmentMap::const_iterator it
= attachments_
.begin();
513 it
!= attachments_
.end(); ++it
) {
514 Attachment
* attachment
= it
->second
.get();
516 base::StringPrintf("|Attachment|attachmentpoint=%04x", it
->first
);
517 attachment
->AddToSignature(texture_manager
, &signature
);
520 if (!framebuffer_combo_complete_map_
) {
521 framebuffer_combo_complete_map_
= new FramebufferComboCompleteMap();
524 FramebufferComboCompleteMap::const_iterator it
=
525 framebuffer_combo_complete_map_
->find(signature
);
526 if (it
!= framebuffer_combo_complete_map_
->end()) {
527 return GL_FRAMEBUFFER_COMPLETE
;
531 GLenum result
= glCheckFramebufferStatusEXT(target
);
533 // Insert the new result into the combo map.
534 if (allow_framebuffer_combo_complete_map_
&&
535 result
== GL_FRAMEBUFFER_COMPLETE
) {
536 framebuffer_combo_complete_map_
->insert(std::make_pair(signature
, true));
542 bool Framebuffer::IsCleared() const {
543 // are all the attachments cleaared?
544 for (AttachmentMap::const_iterator it
= attachments_
.begin();
545 it
!= attachments_
.end(); ++it
) {
546 Attachment
* attachment
= it
->second
.get();
547 if (!attachment
->cleared()) {
554 GLenum
Framebuffer::GetDrawBuffer(GLenum draw_buffer
) const {
555 GLsizei index
= static_cast<GLsizei
>(
556 draw_buffer
- GL_DRAW_BUFFER0_ARB
);
558 index
< static_cast<GLsizei
>(manager_
->max_draw_buffers_
));
559 return draw_buffers_
[index
];
562 void Framebuffer::SetDrawBuffers(GLsizei n
, const GLenum
* bufs
) {
563 DCHECK(n
<= static_cast<GLsizei
>(manager_
->max_draw_buffers_
));
564 for (GLsizei i
= 0; i
< n
; ++i
)
565 draw_buffers_
[i
] = bufs
[i
];
570 bool Framebuffer::HasAlphaMRT() const {
571 for (uint32 i
= 0; i
< manager_
->max_draw_buffers_
; ++i
) {
572 if (draw_buffers_
[i
] != GL_NONE
) {
573 const Attachment
* attachment
= GetAttachment(draw_buffers_
[i
]);
576 if ((GLES2Util::GetChannelsForFormat(
577 attachment
->internal_format()) & 0x0008) != 0)
584 void Framebuffer::UnbindRenderbuffer(
585 GLenum target
, Renderbuffer
* renderbuffer
) {
589 for (AttachmentMap::const_iterator it
= attachments_
.begin();
590 it
!= attachments_
.end(); ++it
) {
591 Attachment
* attachment
= it
->second
.get();
592 if (attachment
->IsRenderbuffer(renderbuffer
)) {
593 // TODO(gman): manually detach renderbuffer.
594 // glFramebufferRenderbufferEXT(target, it->first, GL_RENDERBUFFER, 0);
595 AttachRenderbuffer(it
->first
, NULL
);
603 void Framebuffer::UnbindTexture(
604 GLenum target
, TextureRef
* texture_ref
) {
608 for (AttachmentMap::const_iterator it
= attachments_
.begin();
609 it
!= attachments_
.end(); ++it
) {
610 Attachment
* attachment
= it
->second
.get();
611 if (attachment
->IsTexture(texture_ref
)) {
612 // TODO(gman): manually detach texture.
613 // glFramebufferTexture2DEXT(target, it->first, GL_TEXTURE_2D, 0, 0);
614 AttachTexture(it
->first
, NULL
, GL_TEXTURE_2D
, 0, 0);
622 Framebuffer
* FramebufferManager::GetFramebuffer(
624 FramebufferMap::iterator it
= framebuffers_
.find(client_id
);
625 return it
!= framebuffers_
.end() ? it
->second
.get() : NULL
;
628 void FramebufferManager::RemoveFramebuffer(GLuint client_id
) {
629 FramebufferMap::iterator it
= framebuffers_
.find(client_id
);
630 if (it
!= framebuffers_
.end()) {
631 it
->second
->MarkAsDeleted();
632 framebuffers_
.erase(it
);
636 void Framebuffer::AttachRenderbuffer(
637 GLenum attachment
, Renderbuffer
* renderbuffer
) {
638 const Attachment
* a
= GetAttachment(attachment
);
640 a
->DetachFromFramebuffer(this);
642 attachments_
[attachment
] = scoped_refptr
<Attachment
>(
643 new RenderbufferAttachment(renderbuffer
));
645 attachments_
.erase(attachment
);
647 framebuffer_complete_state_count_id_
= 0;
650 void Framebuffer::AttachTexture(
651 GLenum attachment
, TextureRef
* texture_ref
, GLenum target
,
652 GLint level
, GLsizei samples
) {
653 const Attachment
* a
= GetAttachment(attachment
);
655 a
->DetachFromFramebuffer(this);
657 attachments_
[attachment
] = scoped_refptr
<Attachment
>(
658 new TextureAttachment(texture_ref
, target
, level
, samples
));
659 texture_ref
->texture()->AttachToFramebuffer();
661 attachments_
.erase(attachment
);
663 framebuffer_complete_state_count_id_
= 0;
666 const Framebuffer::Attachment
*
667 Framebuffer::GetAttachment(
668 GLenum attachment
) const {
669 AttachmentMap::const_iterator it
= attachments_
.find(attachment
);
670 if (it
!= attachments_
.end()) {
671 return it
->second
.get();
676 void Framebuffer::OnTextureRefDetached(TextureRef
* texture
) {
677 manager_
->OnTextureRefDetached(texture
);
680 void Framebuffer::OnWillRenderTo() const {
681 for (AttachmentMap::const_iterator it
= attachments_
.begin();
682 it
!= attachments_
.end(); ++it
) {
683 it
->second
->OnWillRenderTo();
687 void Framebuffer::OnDidRenderTo() const {
688 for (AttachmentMap::const_iterator it
= attachments_
.begin();
689 it
!= attachments_
.end(); ++it
) {
690 it
->second
->OnDidRenderTo();
694 bool FramebufferManager::GetClientId(
695 GLuint service_id
, GLuint
* client_id
) const {
696 // This doesn't need to be fast. It's only used during slow queries.
697 for (FramebufferMap::const_iterator it
= framebuffers_
.begin();
698 it
!= framebuffers_
.end(); ++it
) {
699 if (it
->second
->service_id() == service_id
) {
700 *client_id
= it
->first
;
707 void FramebufferManager::MarkAttachmentsAsCleared(
708 Framebuffer
* framebuffer
,
709 RenderbufferManager
* renderbuffer_manager
,
710 TextureManager
* texture_manager
) {
712 framebuffer
->MarkAttachmentsAsCleared(renderbuffer_manager
,
715 MarkAsComplete(framebuffer
);
718 void FramebufferManager::MarkAsComplete(
719 Framebuffer
* framebuffer
) {
721 framebuffer
->MarkAsComplete(framebuffer_state_change_count_
);
724 bool FramebufferManager::IsComplete(
725 Framebuffer
* framebuffer
) {
727 return framebuffer
->framebuffer_complete_state_count_id() ==
728 framebuffer_state_change_count_
;
731 void FramebufferManager::OnTextureRefDetached(TextureRef
* texture
) {
732 for (TextureDetachObserverVector::iterator it
=
733 texture_detach_observers_
.begin();
734 it
!= texture_detach_observers_
.end();
736 TextureDetachObserver
* observer
= *it
;
737 observer
->OnTextureRefDetachedFromFramebuffer(texture
);