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 GLsizei
width() const override
{ return renderbuffer_
->width(); }
53 GLsizei
height() const override
{ return renderbuffer_
->height(); }
55 GLenum
internal_format() const override
{
56 return renderbuffer_
->internal_format();
59 GLenum
texture_type() const override
{ return 0; }
61 GLsizei
samples() const override
{ return renderbuffer_
->samples(); }
63 GLuint
object_name() const override
{ return renderbuffer_
->client_id(); }
65 bool cleared() const override
{ return renderbuffer_
->cleared(); }
67 void SetCleared(RenderbufferManager
* renderbuffer_manager
,
68 TextureManager
* /* texture_manager */,
69 bool cleared
) override
{
70 renderbuffer_manager
->SetCleared(renderbuffer_
.get(), cleared
);
73 bool IsTexture(TextureRef
* /* texture */) const override
{ return false; }
75 bool IsRenderbuffer(Renderbuffer
* renderbuffer
) const override
{
76 return renderbuffer_
.get() == renderbuffer
;
79 bool CanRenderTo() const override
{ return true; }
81 void DetachFromFramebuffer(Framebuffer
* framebuffer
) const override
{
82 // Nothing to do for renderbuffers.
85 bool ValidForAttachmentType(GLenum attachment_type
,
86 uint32 max_color_attachments
) override
{
87 uint32 need
= GLES2Util::GetChannelsNeededForAttachmentType(
88 attachment_type
, max_color_attachments
);
89 uint32 have
= GLES2Util::GetChannelsForFormat(internal_format());
90 return (need
& have
) != 0;
93 Renderbuffer
* renderbuffer() const {
94 return renderbuffer_
.get();
97 size_t GetSignatureSize(TextureManager
* texture_manager
) const override
{
98 return renderbuffer_
->GetSignatureSize();
101 void AddToSignature(TextureManager
* texture_manager
,
102 std::string
* signature
) const override
{
104 renderbuffer_
->AddToSignature(signature
);
107 void OnWillRenderTo() const override
{}
108 void OnDidRenderTo() const override
{}
109 bool FormsFeedbackLoop(TextureRef
* /* texture */,
110 GLint
/*level */) const override
{
115 ~RenderbufferAttachment() override
{}
118 scoped_refptr
<Renderbuffer
> renderbuffer_
;
120 DISALLOW_COPY_AND_ASSIGN(RenderbufferAttachment
);
123 class TextureAttachment
124 : public Framebuffer::Attachment
{
127 TextureRef
* texture_ref
, GLenum target
, GLint level
, GLsizei samples
)
128 : texture_ref_(texture_ref
),
134 GLsizei
width() const override
{
135 GLsizei temp_width
= 0;
136 GLsizei temp_height
= 0;
137 texture_ref_
->texture()->GetLevelSize(
138 target_
, level_
, &temp_width
, &temp_height
);
142 GLsizei
height() const override
{
143 GLsizei temp_width
= 0;
144 GLsizei temp_height
= 0;
145 texture_ref_
->texture()->GetLevelSize(
146 target_
, level_
, &temp_width
, &temp_height
);
150 GLenum
internal_format() const override
{
151 GLenum temp_type
= 0;
152 GLenum temp_internal_format
= 0;
153 texture_ref_
->texture()->GetLevelType(
154 target_
, level_
, &temp_type
, &temp_internal_format
);
155 return temp_internal_format
;
158 GLenum
texture_type() const override
{
159 GLenum temp_type
= 0;
160 GLenum temp_internal_format
= 0;
161 texture_ref_
->texture()->GetLevelType(
162 target_
, level_
, &temp_type
, &temp_internal_format
);
166 GLsizei
samples() const override
{ return samples_
; }
168 GLuint
object_name() const override
{ return texture_ref_
->client_id(); }
170 bool cleared() const override
{
171 return texture_ref_
->texture()->IsLevelCleared(target_
, level_
);
174 void SetCleared(RenderbufferManager
* /* renderbuffer_manager */,
175 TextureManager
* texture_manager
,
176 bool cleared
) override
{
177 texture_manager
->SetLevelCleared(
178 texture_ref_
.get(), target_
, level_
, cleared
);
181 bool IsTexture(TextureRef
* texture
) const override
{
182 return texture
== texture_ref_
.get();
185 bool IsRenderbuffer(Renderbuffer
* /* renderbuffer */) const override
{
189 TextureRef
* texture() const {
190 return texture_ref_
.get();
193 bool CanRenderTo() const override
{
194 return texture_ref_
->texture()->CanRenderTo();
197 void DetachFromFramebuffer(Framebuffer
* framebuffer
) const override
{
198 texture_ref_
->texture()->DetachFromFramebuffer();
199 framebuffer
->OnTextureRefDetached(texture_ref_
.get());
202 bool ValidForAttachmentType(GLenum attachment_type
,
203 uint32 max_color_attachments
) override
{
205 GLenum internal_format
= 0;
206 if (!texture_ref_
->texture()->GetLevelType(
207 target_
, level_
, &type
, &internal_format
)) {
210 uint32 need
= GLES2Util::GetChannelsNeededForAttachmentType(
211 attachment_type
, max_color_attachments
);
212 uint32 have
= GLES2Util::GetChannelsForFormat(internal_format
);
214 // Workaround for NVIDIA drivers that incorrectly expose these formats as
216 if (internal_format
== GL_LUMINANCE
|| internal_format
== GL_ALPHA
||
217 internal_format
== GL_LUMINANCE_ALPHA
) {
220 return (need
& have
) != 0;
223 size_t GetSignatureSize(TextureManager
* texture_manager
) const override
{
224 return texture_manager
->GetSignatureSize();
227 void AddToSignature(TextureManager
* texture_manager
,
228 std::string
* signature
) const override
{
230 texture_manager
->AddToSignature(
231 texture_ref_
.get(), target_
, level_
, signature
);
234 void OnWillRenderTo() const override
{
235 texture_ref_
->texture()->OnWillModifyPixels();
238 void OnDidRenderTo() const override
{
239 texture_ref_
->texture()->OnDidModifyPixels();
242 bool FormsFeedbackLoop(TextureRef
* texture
, GLint level
) const override
{
243 return texture
== texture_ref_
.get() && level
== level_
;
247 ~TextureAttachment() override
{}
250 scoped_refptr
<TextureRef
> texture_ref_
;
255 DISALLOW_COPY_AND_ASSIGN(TextureAttachment
);
258 FramebufferManager::TextureDetachObserver::TextureDetachObserver() {}
260 FramebufferManager::TextureDetachObserver::~TextureDetachObserver() {}
262 FramebufferManager::FramebufferManager(
263 uint32 max_draw_buffers
, uint32 max_color_attachments
)
264 : framebuffer_state_change_count_(1),
265 framebuffer_count_(0),
267 max_draw_buffers_(max_draw_buffers
),
268 max_color_attachments_(max_color_attachments
) {
269 DCHECK_GT(max_draw_buffers_
, 0u);
270 DCHECK_GT(max_color_attachments_
, 0u);
273 FramebufferManager::~FramebufferManager() {
274 DCHECK(framebuffers_
.empty());
275 // If this triggers, that means something is keeping a reference to a
276 // Framebuffer belonging to this.
277 CHECK_EQ(framebuffer_count_
, 0u);
280 void Framebuffer::MarkAsDeleted() {
282 while (!attachments_
.empty()) {
283 Attachment
* attachment
= attachments_
.begin()->second
.get();
284 attachment
->DetachFromFramebuffer(this);
285 attachments_
.erase(attachments_
.begin());
289 void FramebufferManager::Destroy(bool have_context
) {
290 have_context_
= have_context
;
291 framebuffers_
.clear();
294 void FramebufferManager::StartTracking(
295 Framebuffer
* /* framebuffer */) {
296 ++framebuffer_count_
;
299 void FramebufferManager::StopTracking(
300 Framebuffer
* /* framebuffer */) {
301 --framebuffer_count_
;
304 void FramebufferManager::CreateFramebuffer(
305 GLuint client_id
, GLuint service_id
) {
306 std::pair
<FramebufferMap::iterator
, bool> result
=
307 framebuffers_
.insert(
310 scoped_refptr
<Framebuffer
>(
311 new Framebuffer(this, service_id
))));
312 DCHECK(result
.second
);
315 Framebuffer::Framebuffer(
316 FramebufferManager
* manager
, GLuint service_id
)
319 service_id_(service_id
),
320 has_been_bound_(false),
321 framebuffer_complete_state_count_id_(0) {
322 manager
->StartTracking(this);
323 DCHECK_GT(manager
->max_draw_buffers_
, 0u);
324 draw_buffers_
.reset(new GLenum
[manager
->max_draw_buffers_
]);
325 draw_buffers_
[0] = GL_COLOR_ATTACHMENT0
;
326 for (uint32 i
= 1; i
< manager
->max_draw_buffers_
; ++i
)
327 draw_buffers_
[i
] = GL_NONE
;
330 Framebuffer::~Framebuffer() {
332 if (manager_
->have_context_
) {
333 GLuint id
= service_id();
334 glDeleteFramebuffersEXT(1, &id
);
336 manager_
->StopTracking(this);
341 bool Framebuffer::HasUnclearedAttachment(
342 GLenum attachment
) const {
343 AttachmentMap::const_iterator it
=
344 attachments_
.find(attachment
);
345 if (it
!= attachments_
.end()) {
346 const Attachment
* attachment
= it
->second
.get();
347 return !attachment
->cleared();
352 bool Framebuffer::HasUnclearedColorAttachments() const {
353 for (AttachmentMap::const_iterator it
= attachments_
.begin();
354 it
!= attachments_
.end(); ++it
) {
355 if (it
->first
>= GL_COLOR_ATTACHMENT0
&&
356 it
->first
< GL_COLOR_ATTACHMENT0
+ manager_
->max_draw_buffers_
) {
357 const Attachment
* attachment
= it
->second
.get();
358 if (!attachment
->cleared())
365 void Framebuffer::ChangeDrawBuffersHelper(bool recover
) const {
366 scoped_ptr
<GLenum
[]> buffers(new GLenum
[manager_
->max_draw_buffers_
]);
367 for (uint32 i
= 0; i
< manager_
->max_draw_buffers_
; ++i
)
368 buffers
[i
] = GL_NONE
;
369 for (AttachmentMap::const_iterator it
= attachments_
.begin();
370 it
!= attachments_
.end(); ++it
) {
371 if (it
->first
>= GL_COLOR_ATTACHMENT0
&&
372 it
->first
< GL_COLOR_ATTACHMENT0
+ manager_
->max_draw_buffers_
) {
373 buffers
[it
->first
- GL_COLOR_ATTACHMENT0
] = it
->first
;
376 bool different
= false;
377 for (uint32 i
= 0; i
< manager_
->max_draw_buffers_
; ++i
) {
378 if (buffers
[i
] != draw_buffers_
[i
]) {
385 glDrawBuffersARB(manager_
->max_draw_buffers_
, draw_buffers_
.get());
387 glDrawBuffersARB(manager_
->max_draw_buffers_
, buffers
.get());
391 void Framebuffer::PrepareDrawBuffersForClear() const {
392 bool recover
= false;
393 ChangeDrawBuffersHelper(recover
);
396 void Framebuffer::RestoreDrawBuffersAfterClear() const {
398 ChangeDrawBuffersHelper(recover
);
401 void Framebuffer::MarkAttachmentAsCleared(
402 RenderbufferManager
* renderbuffer_manager
,
403 TextureManager
* texture_manager
,
406 AttachmentMap::iterator it
= attachments_
.find(attachment
);
407 if (it
!= attachments_
.end()) {
408 Attachment
* a
= it
->second
.get();
409 if (a
->cleared() != cleared
) {
410 a
->SetCleared(renderbuffer_manager
,
417 void Framebuffer::MarkAttachmentsAsCleared(
418 RenderbufferManager
* renderbuffer_manager
,
419 TextureManager
* texture_manager
,
421 for (AttachmentMap::iterator it
= attachments_
.begin();
422 it
!= attachments_
.end(); ++it
) {
423 Attachment
* attachment
= it
->second
.get();
424 if (attachment
->cleared() != cleared
) {
425 attachment
->SetCleared(renderbuffer_manager
, texture_manager
, cleared
);
430 bool Framebuffer::HasDepthAttachment() const {
431 return attachments_
.find(GL_DEPTH_STENCIL_ATTACHMENT
) != attachments_
.end() ||
432 attachments_
.find(GL_DEPTH_ATTACHMENT
) != attachments_
.end();
435 bool Framebuffer::HasStencilAttachment() const {
436 return attachments_
.find(GL_DEPTH_STENCIL_ATTACHMENT
) != attachments_
.end() ||
437 attachments_
.find(GL_STENCIL_ATTACHMENT
) != attachments_
.end();
440 GLenum
Framebuffer::GetColorAttachmentFormat() const {
441 AttachmentMap::const_iterator it
= attachments_
.find(GL_COLOR_ATTACHMENT0
);
442 if (it
== attachments_
.end()) {
445 const Attachment
* attachment
= it
->second
.get();
446 return attachment
->internal_format();
449 GLenum
Framebuffer::GetColorAttachmentTextureType() const {
450 AttachmentMap::const_iterator it
= attachments_
.find(GL_COLOR_ATTACHMENT0
);
451 if (it
== attachments_
.end()) {
454 const Attachment
* attachment
= it
->second
.get();
455 return attachment
->texture_type();
458 GLenum
Framebuffer::IsPossiblyComplete() const {
459 if (attachments_
.empty()) {
460 return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
;
465 for (AttachmentMap::const_iterator it
= attachments_
.begin();
466 it
!= attachments_
.end(); ++it
) {
467 GLenum attachment_type
= it
->first
;
468 Attachment
* attachment
= it
->second
.get();
469 if (!attachment
->ValidForAttachmentType(attachment_type
,
470 manager_
->max_color_attachments_
)) {
471 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
;
474 width
= attachment
->width();
475 height
= attachment
->height();
476 if (width
== 0 || height
== 0) {
477 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
;
480 if (attachment
->width() != width
|| attachment
->height() != height
) {
481 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT
;
485 if (!attachment
->CanRenderTo()) {
486 return GL_FRAMEBUFFER_UNSUPPORTED
;
490 // This does not mean the framebuffer is actually complete. It just means our
492 return GL_FRAMEBUFFER_COMPLETE
;
495 GLenum
Framebuffer::GetStatus(
496 TextureManager
* texture_manager
, GLenum target
) const {
497 // Check if we have this combo already.
498 std::string signature
;
499 if (allow_framebuffer_combo_complete_map_
) {
500 size_t signature_size
= sizeof(target
);
501 for (AttachmentMap::const_iterator it
= attachments_
.begin();
502 it
!= attachments_
.end(); ++it
) {
503 Attachment
* attachment
= it
->second
.get();
504 signature_size
+= sizeof(it
->first
) +
505 attachment
->GetSignatureSize(texture_manager
);
508 signature
.reserve(signature_size
);
509 signature
.append(reinterpret_cast<const char*>(&target
), sizeof(target
));
511 for (AttachmentMap::const_iterator it
= attachments_
.begin();
512 it
!= attachments_
.end(); ++it
) {
513 Attachment
* attachment
= it
->second
.get();
514 signature
.append(reinterpret_cast<const char*>(&it
->first
),
516 attachment
->AddToSignature(texture_manager
, &signature
);
518 DCHECK(signature
.size() == signature_size
);
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
];
568 bool Framebuffer::HasAlphaMRT() const {
569 for (uint32 i
= 0; i
< manager_
->max_draw_buffers_
; ++i
) {
570 if (draw_buffers_
[i
] != GL_NONE
) {
571 const Attachment
* attachment
= GetAttachment(draw_buffers_
[i
]);
574 if ((GLES2Util::GetChannelsForFormat(
575 attachment
->internal_format()) & 0x0008) != 0)
582 void Framebuffer::UnbindRenderbuffer(
583 GLenum target
, Renderbuffer
* renderbuffer
) {
587 for (AttachmentMap::const_iterator it
= attachments_
.begin();
588 it
!= attachments_
.end(); ++it
) {
589 Attachment
* attachment
= it
->second
.get();
590 if (attachment
->IsRenderbuffer(renderbuffer
)) {
591 // TODO(gman): manually detach renderbuffer.
592 // glFramebufferRenderbufferEXT(target, it->first, GL_RENDERBUFFER, 0);
593 AttachRenderbuffer(it
->first
, NULL
);
601 void Framebuffer::UnbindTexture(
602 GLenum target
, TextureRef
* texture_ref
) {
606 for (AttachmentMap::const_iterator it
= attachments_
.begin();
607 it
!= attachments_
.end(); ++it
) {
608 Attachment
* attachment
= it
->second
.get();
609 if (attachment
->IsTexture(texture_ref
)) {
610 // TODO(gman): manually detach texture.
611 // glFramebufferTexture2DEXT(target, it->first, GL_TEXTURE_2D, 0, 0);
612 AttachTexture(it
->first
, NULL
, GL_TEXTURE_2D
, 0, 0);
620 Framebuffer
* FramebufferManager::GetFramebuffer(
622 FramebufferMap::iterator it
= framebuffers_
.find(client_id
);
623 return it
!= framebuffers_
.end() ? it
->second
.get() : NULL
;
626 void FramebufferManager::RemoveFramebuffer(GLuint client_id
) {
627 FramebufferMap::iterator it
= framebuffers_
.find(client_id
);
628 if (it
!= framebuffers_
.end()) {
629 it
->second
->MarkAsDeleted();
630 framebuffers_
.erase(it
);
634 void Framebuffer::DoUnbindGLAttachmentsForWorkaround(GLenum target
) {
635 // Replace all attachments with the default Renderbuffer.
636 for (AttachmentMap::const_iterator it
= attachments_
.begin();
637 it
!= attachments_
.end(); ++it
) {
638 glFramebufferRenderbufferEXT(target
, it
->first
, GL_RENDERBUFFER
, 0);
642 void Framebuffer::AttachRenderbuffer(
643 GLenum attachment
, Renderbuffer
* renderbuffer
) {
644 const Attachment
* a
= GetAttachment(attachment
);
646 a
->DetachFromFramebuffer(this);
648 attachments_
[attachment
] = scoped_refptr
<Attachment
>(
649 new RenderbufferAttachment(renderbuffer
));
651 attachments_
.erase(attachment
);
653 framebuffer_complete_state_count_id_
= 0;
656 void Framebuffer::AttachTexture(
657 GLenum attachment
, TextureRef
* texture_ref
, GLenum target
,
658 GLint level
, GLsizei samples
) {
659 const Attachment
* a
= GetAttachment(attachment
);
661 a
->DetachFromFramebuffer(this);
663 attachments_
[attachment
] = scoped_refptr
<Attachment
>(
664 new TextureAttachment(texture_ref
, target
, level
, samples
));
665 texture_ref
->texture()->AttachToFramebuffer();
667 attachments_
.erase(attachment
);
669 framebuffer_complete_state_count_id_
= 0;
672 const Framebuffer::Attachment
*
673 Framebuffer::GetAttachment(
674 GLenum attachment
) const {
675 AttachmentMap::const_iterator it
= attachments_
.find(attachment
);
676 if (it
!= attachments_
.end()) {
677 return it
->second
.get();
682 void Framebuffer::OnTextureRefDetached(TextureRef
* texture
) {
683 manager_
->OnTextureRefDetached(texture
);
686 void Framebuffer::OnWillRenderTo() const {
687 for (AttachmentMap::const_iterator it
= attachments_
.begin();
688 it
!= attachments_
.end(); ++it
) {
689 it
->second
->OnWillRenderTo();
693 void Framebuffer::OnDidRenderTo() const {
694 for (AttachmentMap::const_iterator it
= attachments_
.begin();
695 it
!= attachments_
.end(); ++it
) {
696 it
->second
->OnDidRenderTo();
700 bool FramebufferManager::GetClientId(
701 GLuint service_id
, GLuint
* client_id
) const {
702 // This doesn't need to be fast. It's only used during slow queries.
703 for (FramebufferMap::const_iterator it
= framebuffers_
.begin();
704 it
!= framebuffers_
.end(); ++it
) {
705 if (it
->second
->service_id() == service_id
) {
706 *client_id
= it
->first
;
713 void FramebufferManager::MarkAttachmentsAsCleared(
714 Framebuffer
* framebuffer
,
715 RenderbufferManager
* renderbuffer_manager
,
716 TextureManager
* texture_manager
) {
718 framebuffer
->MarkAttachmentsAsCleared(renderbuffer_manager
,
721 MarkAsComplete(framebuffer
);
724 void FramebufferManager::MarkAsComplete(
725 Framebuffer
* framebuffer
) {
727 framebuffer
->MarkAsComplete(framebuffer_state_change_count_
);
730 bool FramebufferManager::IsComplete(
731 Framebuffer
* framebuffer
) {
733 return framebuffer
->framebuffer_complete_state_count_id() ==
734 framebuffer_state_change_count_
;
737 void FramebufferManager::OnTextureRefDetached(TextureRef
* texture
) {
738 for (TextureDetachObserverVector::iterator it
=
739 texture_detach_observers_
.begin();
740 it
!= texture_detach_observers_
.end();
742 TextureDetachObserver
* observer
= *it
;
743 observer
->OnTextureRefDetachedFromFramebuffer(texture
);