Home | History | Annotate | Download | only in canvas
      1 /*
      2  * Copyright (C) 2009 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #include "core/html/canvas/WebGLFramebuffer.h"
     29 
     30 #include "core/html/canvas/WebGLRenderingContext.h"
     31 #include "core/platform/NotImplemented.h"
     32 #include "core/platform/graphics/Extensions3D.h"
     33 
     34 namespace WebCore {
     35 
     36 namespace {
     37 
     38     Platform3DObject objectOrZero(WebGLObject* object)
     39     {
     40         return object ? object->object() : 0;
     41     }
     42 
     43     class WebGLRenderbufferAttachment : public WebGLFramebuffer::WebGLAttachment {
     44     public:
     45         static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLRenderbuffer*);
     46 
     47     private:
     48         WebGLRenderbufferAttachment(WebGLRenderbuffer*);
     49         virtual GC3Dsizei getWidth() const;
     50         virtual GC3Dsizei getHeight() const;
     51         virtual GC3Denum getFormat() const;
     52         virtual GC3Denum getType() const;
     53         virtual WebGLSharedObject* getObject() const;
     54         virtual bool isSharedObject(WebGLSharedObject*) const;
     55         virtual bool isValid() const;
     56         virtual bool isInitialized() const;
     57         virtual void setInitialized();
     58         virtual void onDetached(GraphicsContext3D*);
     59         virtual void attach(GraphicsContext3D*, GC3Denum attachment);
     60         virtual void unattach(GraphicsContext3D*, GC3Denum attachment);
     61 
     62         WebGLRenderbufferAttachment() { };
     63 
     64         RefPtr<WebGLRenderbuffer> m_renderbuffer;
     65     };
     66 
     67     PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLRenderbufferAttachment::create(WebGLRenderbuffer* renderbuffer)
     68     {
     69         return adoptRef(new WebGLRenderbufferAttachment(renderbuffer));
     70     }
     71 
     72     WebGLRenderbufferAttachment::WebGLRenderbufferAttachment(WebGLRenderbuffer* renderbuffer)
     73         : m_renderbuffer(renderbuffer)
     74     {
     75     }
     76 
     77     GC3Dsizei WebGLRenderbufferAttachment::getWidth() const
     78     {
     79         return m_renderbuffer->getWidth();
     80     }
     81 
     82     GC3Dsizei WebGLRenderbufferAttachment::getHeight() const
     83     {
     84         return m_renderbuffer->getHeight();
     85     }
     86 
     87     GC3Denum WebGLRenderbufferAttachment::getFormat() const
     88     {
     89         GC3Denum format = m_renderbuffer->getInternalFormat();
     90         if (format == GraphicsContext3D::DEPTH_STENCIL
     91             && m_renderbuffer->emulatedStencilBuffer()
     92             && m_renderbuffer->emulatedStencilBuffer()->getInternalFormat() != GraphicsContext3D::STENCIL_INDEX8) {
     93             return 0;
     94         }
     95         return format;
     96     }
     97 
     98     WebGLSharedObject* WebGLRenderbufferAttachment::getObject() const
     99     {
    100         return m_renderbuffer->object() ? m_renderbuffer.get() : 0;
    101     }
    102 
    103     bool WebGLRenderbufferAttachment::isSharedObject(WebGLSharedObject* object) const
    104     {
    105         return object == m_renderbuffer;
    106     }
    107 
    108     bool WebGLRenderbufferAttachment::isValid() const
    109     {
    110         return m_renderbuffer->object();
    111     }
    112 
    113     bool WebGLRenderbufferAttachment::isInitialized() const
    114     {
    115         return m_renderbuffer->object() && m_renderbuffer->isInitialized();
    116     }
    117 
    118     void WebGLRenderbufferAttachment::setInitialized()
    119     {
    120         if (m_renderbuffer->object())
    121             m_renderbuffer->setInitialized();
    122     }
    123 
    124     void WebGLRenderbufferAttachment::onDetached(GraphicsContext3D* context)
    125     {
    126         m_renderbuffer->onDetached(context);
    127     }
    128 
    129     void WebGLRenderbufferAttachment::attach(GraphicsContext3D* context, GC3Denum attachment)
    130     {
    131         Platform3DObject object = objectOrZero(m_renderbuffer.get());
    132         if (attachment == GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT && m_renderbuffer->emulatedStencilBuffer()) {
    133             context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, object);
    134             context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, objectOrZero(m_renderbuffer->emulatedStencilBuffer()));
    135         } else {
    136             context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, attachment, GraphicsContext3D::RENDERBUFFER, object);
    137         }
    138     }
    139 
    140     void WebGLRenderbufferAttachment::unattach(GraphicsContext3D* context, GC3Denum attachment)
    141     {
    142         if (attachment == GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT) {
    143             context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0);
    144             context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0);
    145         } else
    146             context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, attachment, GraphicsContext3D::RENDERBUFFER, 0);
    147     }
    148 
    149     GC3Denum WebGLRenderbufferAttachment::getType() const
    150     {
    151         notImplemented();
    152         return 0;
    153     }
    154 
    155     class WebGLTextureAttachment : public WebGLFramebuffer::WebGLAttachment {
    156     public:
    157         static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLTexture*, GC3Denum target, GC3Dint level);
    158 
    159     private:
    160         WebGLTextureAttachment(WebGLTexture*, GC3Denum target, GC3Dint level);
    161         virtual GC3Dsizei getWidth() const;
    162         virtual GC3Dsizei getHeight() const;
    163         virtual GC3Denum getFormat() const;
    164         virtual GC3Denum getType() const;
    165         virtual WebGLSharedObject* getObject() const;
    166         virtual bool isSharedObject(WebGLSharedObject*) const;
    167         virtual bool isValid() const;
    168         virtual bool isInitialized() const;
    169         virtual void setInitialized();
    170         virtual void onDetached(GraphicsContext3D*);
    171         virtual void attach(GraphicsContext3D*, GC3Denum attachment);
    172         virtual void unattach(GraphicsContext3D*, GC3Denum attachment);
    173 
    174         WebGLTextureAttachment() { };
    175 
    176         RefPtr<WebGLTexture> m_texture;
    177         GC3Denum m_target;
    178         GC3Dint m_level;
    179     };
    180 
    181     PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLTextureAttachment::create(WebGLTexture* texture, GC3Denum target, GC3Dint level)
    182     {
    183         return adoptRef(new WebGLTextureAttachment(texture, target, level));
    184     }
    185 
    186     WebGLTextureAttachment::WebGLTextureAttachment(WebGLTexture* texture, GC3Denum target, GC3Dint level)
    187         : m_texture(texture)
    188         , m_target(target)
    189         , m_level(level)
    190     {
    191     }
    192 
    193     GC3Dsizei WebGLTextureAttachment::getWidth() const
    194     {
    195         return m_texture->getWidth(m_target, m_level);
    196     }
    197 
    198     GC3Dsizei WebGLTextureAttachment::getHeight() const
    199     {
    200         return m_texture->getHeight(m_target, m_level);
    201     }
    202 
    203     GC3Denum WebGLTextureAttachment::getFormat() const
    204     {
    205         return m_texture->getInternalFormat(m_target, m_level);
    206     }
    207 
    208     WebGLSharedObject* WebGLTextureAttachment::getObject() const
    209     {
    210         return m_texture->object() ? m_texture.get() : 0;
    211     }
    212 
    213     bool WebGLTextureAttachment::isSharedObject(WebGLSharedObject* object) const
    214     {
    215         return object == m_texture;
    216     }
    217 
    218     bool WebGLTextureAttachment::isValid() const
    219     {
    220         return m_texture->object();
    221     }
    222 
    223     bool WebGLTextureAttachment::isInitialized() const
    224     {
    225         // Textures are assumed to be initialized.
    226         return true;
    227     }
    228 
    229     void WebGLTextureAttachment::setInitialized()
    230     {
    231         // Textures are assumed to be initialized.
    232     }
    233 
    234     void WebGLTextureAttachment::onDetached(GraphicsContext3D* context)
    235     {
    236         m_texture->onDetached(context);
    237     }
    238 
    239     void WebGLTextureAttachment::attach(GraphicsContext3D* context, GC3Denum attachment)
    240     {
    241         Platform3DObject object = objectOrZero(m_texture.get());
    242         context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, attachment, m_target, object, m_level);
    243     }
    244 
    245     void WebGLTextureAttachment::unattach(GraphicsContext3D* context, GC3Denum attachment)
    246     {
    247         if (attachment == GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT) {
    248             context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, m_target, 0, m_level);
    249             context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, m_target, 0, m_level);
    250         } else
    251             context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, attachment, m_target, 0, m_level);
    252     }
    253 
    254     GC3Denum WebGLTextureAttachment::getType() const
    255     {
    256         return m_texture->getType(m_target, m_level);
    257     }
    258 
    259     bool isColorRenderable(GC3Denum internalformat)
    260     {
    261         switch (internalformat) {
    262         case GraphicsContext3D::RGBA4:
    263         case GraphicsContext3D::RGB5_A1:
    264         case GraphicsContext3D::RGB565:
    265             return true;
    266         default:
    267             return false;
    268         }
    269     }
    270 
    271 } // anonymous namespace
    272 
    273 WebGLFramebuffer::WebGLAttachment::WebGLAttachment()
    274 {
    275 }
    276 
    277 WebGLFramebuffer::WebGLAttachment::~WebGLAttachment()
    278 {
    279 }
    280 
    281 PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx)
    282 {
    283     return adoptRef(new WebGLFramebuffer(ctx));
    284 }
    285 
    286 WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx)
    287     : WebGLContextObject(ctx)
    288     , m_hasEverBeenBound(false)
    289 {
    290     ScriptWrappable::init(this);
    291     setObject(ctx->graphicsContext3D()->createFramebuffer());
    292 }
    293 
    294 WebGLFramebuffer::~WebGLFramebuffer()
    295 {
    296     deleteObject(0);
    297 }
    298 
    299 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, GC3Denum texTarget, WebGLTexture* texture, GC3Dint level)
    300 {
    301     ASSERT(isBound());
    302     removeAttachmentFromBoundFramebuffer(attachment);
    303     if (!object())
    304         return;
    305     if (texture && texture->object()) {
    306         m_attachments.add(attachment, WebGLTextureAttachment::create(texture, texTarget, level));
    307         drawBuffersIfNecessary(false);
    308         texture->onAttached();
    309     }
    310 }
    311 
    312 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, WebGLRenderbuffer* renderbuffer)
    313 {
    314     ASSERT(isBound());
    315     removeAttachmentFromBoundFramebuffer(attachment);
    316     if (!object())
    317         return;
    318     if (renderbuffer && renderbuffer->object()) {
    319         m_attachments.add(attachment, WebGLRenderbufferAttachment::create(renderbuffer));
    320         drawBuffersIfNecessary(false);
    321         renderbuffer->onAttached();
    322     }
    323 }
    324 
    325 void WebGLFramebuffer::attach(GC3Denum attachment, GC3Denum attachmentPoint)
    326 {
    327     ASSERT(isBound());
    328     WebGLAttachment* attachmentObject = getAttachment(attachment);
    329     if (attachmentObject)
    330         attachmentObject->attach(context()->graphicsContext3D(), attachmentPoint);
    331 }
    332 
    333 WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GC3Denum attachment) const
    334 {
    335     if (!object())
    336         return 0;
    337     WebGLAttachment* attachmentObject = getAttachment(attachment);
    338     return attachmentObject ? attachmentObject->getObject() : 0;
    339 }
    340 
    341 bool WebGLFramebuffer::isAttachmentComplete(WebGLAttachment* attachedObject, GC3Denum attachment, const char** reason) const
    342 {
    343     ASSERT(attachedObject && attachedObject->isValid());
    344     ASSERT(reason);
    345 
    346     GC3Denum internalformat = attachedObject->getFormat();
    347     WebGLSharedObject* object = attachedObject->getObject();
    348     ASSERT(object && (object->isTexture() || object->isRenderbuffer()));
    349 
    350     if (attachment == GraphicsContext3D::DEPTH_ATTACHMENT) {
    351         if (object->isRenderbuffer()) {
    352             if (internalformat != GraphicsContext3D::DEPTH_COMPONENT16) {
    353                 *reason = "the internalformat of the attached renderbuffer is not DEPTH_COMPONENT16";
    354                 return false;
    355             }
    356         } else if (object->isTexture()) {
    357             GC3Denum type = attachedObject->getType();
    358             if (!(context()->m_webglDepthTexture && internalformat == GraphicsContext3D::DEPTH_COMPONENT
    359                 && (type == GraphicsContext3D::UNSIGNED_SHORT || type == GraphicsContext3D::UNSIGNED_INT))) {
    360                 *reason = "the attached texture is not a depth texture";
    361                 return false;
    362             }
    363         }
    364     } else if (attachment == GraphicsContext3D::STENCIL_ATTACHMENT) {
    365         // Depend on the underlying GL drivers to check stencil textures
    366         // and check renderbuffer type here only.
    367         if (object->isRenderbuffer()) {
    368             if (internalformat != GraphicsContext3D::STENCIL_INDEX8) {
    369                 *reason = "the internalformat of the attached renderbuffer is not STENCIL_INDEX8";
    370                 return false;
    371             }
    372         }
    373     } else if (attachment == GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT) {
    374         if (object->isRenderbuffer()) {
    375             if (internalformat != GraphicsContext3D::DEPTH_STENCIL) {
    376                 *reason = "the internalformat of the attached renderbuffer is not DEPTH_STENCIL";
    377                 return false;
    378             }
    379         } else if (object->isTexture()) {
    380             GC3Denum type = attachedObject->getType();
    381             if (!(context()->m_webglDepthTexture && internalformat == GraphicsContext3D::DEPTH_STENCIL
    382                 && type == GraphicsContext3D::UNSIGNED_INT_24_8)) {
    383                 *reason = "the attached texture is not a DEPTH_STENCIL texture";
    384                 return false;
    385             }
    386         }
    387     } else if (attachment == GraphicsContext3D::COLOR_ATTACHMENT0
    388         || (context()->m_webglDrawBuffers && attachment > GraphicsContext3D::COLOR_ATTACHMENT0
    389             && attachment < static_cast<GC3Denum>(GraphicsContext3D::COLOR_ATTACHMENT0 + context()->getMaxColorAttachments()))) {
    390         if (object->isRenderbuffer()) {
    391             if (!isColorRenderable(internalformat)) {
    392                 *reason = "the internalformat of the attached renderbuffer is not color-renderable";
    393                 return false;
    394             }
    395         } else if (object->isTexture()) {
    396             GC3Denum type = attachedObject->getType();
    397             if (internalformat != GraphicsContext3D::RGBA && internalformat != GraphicsContext3D::RGB) {
    398                 *reason = "the internalformat of the attached texture is not color-renderable";
    399                 return false;
    400             }
    401             // TODO: WEBGL_color_buffer_float and EXT_color_buffer_half_float extensions have not been implemented in
    402             // WebGL yet. It would be better to depend on the underlying GL drivers to check on rendering to floating point textures
    403             // and add the check back to WebGL when above two extensions are implemented.
    404             // Assume UNSIGNED_BYTE is renderable here without the need to explicitly check if GL_OES_rgb8_rgba8 extension is supported.
    405             if (type != GraphicsContext3D::UNSIGNED_BYTE
    406                 && type != GraphicsContext3D::UNSIGNED_SHORT_5_6_5
    407                 && type != GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4
    408                 && type != GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1
    409                 && !(type == GraphicsContext3D::FLOAT && context()->m_oesTextureFloat)
    410                 && !(type == GraphicsContext3D::HALF_FLOAT_OES && context()->m_oesTextureHalfFloat)) {
    411                 *reason = "unsupported type: The attached texture is not supported to be rendered to";
    412                 return false;
    413             }
    414         }
    415     } else {
    416         *reason = "unknown framebuffer attachment point";
    417         return false;
    418     }
    419 
    420     if (!attachedObject->getWidth() || !attachedObject->getHeight()) {
    421         *reason = "attachment has a 0 dimension";
    422         return false;
    423     }
    424     return true;
    425 }
    426 
    427 WebGLFramebuffer::WebGLAttachment* WebGLFramebuffer::getAttachment(GC3Denum attachment) const
    428 {
    429     const AttachmentMap::const_iterator it = m_attachments.find(attachment);
    430     return (it != m_attachments.end()) ? it->value.get() : 0;
    431 }
    432 
    433 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GC3Denum attachment)
    434 {
    435     ASSERT(isBound());
    436     if (!object())
    437         return;
    438 
    439     WebGLAttachment* attachmentObject = getAttachment(attachment);
    440     if (attachmentObject) {
    441         attachmentObject->onDetached(context()->graphicsContext3D());
    442         m_attachments.remove(attachment);
    443         drawBuffersIfNecessary(false);
    444         switch (attachment) {
    445         case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
    446             attach(GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::DEPTH_ATTACHMENT);
    447             attach(GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::STENCIL_ATTACHMENT);
    448             break;
    449         case GraphicsContext3D::DEPTH_ATTACHMENT:
    450             attach(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, GraphicsContext3D::DEPTH_ATTACHMENT);
    451             break;
    452         case GraphicsContext3D::STENCIL_ATTACHMENT:
    453             attach(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, GraphicsContext3D::STENCIL_ATTACHMENT);
    454             break;
    455         }
    456     }
    457 }
    458 
    459 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLSharedObject* attachment)
    460 {
    461     ASSERT(isBound());
    462     if (!object())
    463         return;
    464     if (!attachment)
    465         return;
    466 
    467     bool checkMore = true;
    468     while (checkMore) {
    469         checkMore = false;
    470         for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
    471             WebGLAttachment* attachmentObject = it->value.get();
    472             if (attachmentObject->isSharedObject(attachment)) {
    473                 GC3Denum attachmentType = it->key;
    474                 attachmentObject->unattach(context()->graphicsContext3D(), attachmentType);
    475                 removeAttachmentFromBoundFramebuffer(attachmentType);
    476                 checkMore = true;
    477                 break;
    478             }
    479         }
    480     }
    481 }
    482 
    483 GC3Dsizei WebGLFramebuffer::getColorBufferWidth() const
    484 {
    485     if (!object())
    486         return 0;
    487     WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0);
    488     if (!attachment)
    489         return 0;
    490 
    491     return attachment->getWidth();
    492 }
    493 
    494 GC3Dsizei WebGLFramebuffer::getColorBufferHeight() const
    495 {
    496     if (!object())
    497         return 0;
    498     WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0);
    499     if (!attachment)
    500         return 0;
    501 
    502     return attachment->getHeight();
    503 }
    504 
    505 GC3Denum WebGLFramebuffer::getColorBufferFormat() const
    506 {
    507     if (!object())
    508         return 0;
    509     WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0);
    510     if (!attachment)
    511         return 0;
    512     return attachment->getFormat();
    513 }
    514 
    515 GC3Denum WebGLFramebuffer::checkStatus(const char** reason) const
    516 {
    517     unsigned int count = 0;
    518     GC3Dsizei width = 0, height = 0;
    519     bool haveDepth = false;
    520     bool haveStencil = false;
    521     bool haveDepthStencil = false;
    522     for (AttachmentMap::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
    523         WebGLAttachment* attachment = it->value.get();
    524         if (!isAttachmentComplete(attachment, it->key, reason))
    525             return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
    526         if (!attachment->isValid()) {
    527             *reason = "attachment is not valid";
    528             return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED;
    529         }
    530         if (!attachment->getFormat()) {
    531             *reason = "attachment is an unsupported format";
    532             return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
    533         }
    534         switch (it->key) {
    535         case GraphicsContext3D::DEPTH_ATTACHMENT:
    536             haveDepth = true;
    537             break;
    538         case GraphicsContext3D::STENCIL_ATTACHMENT:
    539             haveStencil = true;
    540             break;
    541         case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
    542             haveDepthStencil = true;
    543             break;
    544         }
    545         if (!count) {
    546             width = attachment->getWidth();
    547             height = attachment->getHeight();
    548         } else {
    549             if (width != attachment->getWidth() || height != attachment->getHeight()) {
    550                 *reason = "attachments do not have the same dimensions";
    551                 return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
    552             }
    553         }
    554         ++count;
    555     }
    556     if (!count) {
    557         *reason = "no attachments";
    558         return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
    559     }
    560     if (!width || !height) {
    561         *reason = "framebuffer has a 0 dimension";
    562         return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
    563     }
    564     // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
    565     if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) {
    566         *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments";
    567         return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED;
    568     }
    569     return GraphicsContext3D::FRAMEBUFFER_COMPLETE;
    570 }
    571 
    572 bool WebGLFramebuffer::onAccess(GraphicsContext3D* context3d, const char** reason)
    573 {
    574     if (checkStatus(reason) != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
    575         return false;
    576     return true;
    577 }
    578 
    579 bool WebGLFramebuffer::hasStencilBuffer() const
    580 {
    581     WebGLAttachment* attachment = getAttachment(GraphicsContext3D::STENCIL_ATTACHMENT);
    582     if (!attachment)
    583         attachment = getAttachment(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT);
    584     return attachment && attachment->isValid();
    585 }
    586 
    587 void WebGLFramebuffer::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object)
    588 {
    589     for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
    590         it->value->onDetached(context3d);
    591 
    592     context3d->deleteFramebuffer(object);
    593 }
    594 
    595 bool WebGLFramebuffer::isBound() const
    596 {
    597     return (context()->m_framebufferBinding.get() == this);
    598 }
    599 
    600 void WebGLFramebuffer::drawBuffers(const Vector<GC3Denum>& bufs)
    601 {
    602     m_drawBuffers = bufs;
    603     m_filteredDrawBuffers.resize(m_drawBuffers.size());
    604     for (size_t i = 0; i < m_filteredDrawBuffers.size(); ++i)
    605         m_filteredDrawBuffers[i] = GraphicsContext3D::NONE;
    606     drawBuffersIfNecessary(true);
    607 }
    608 
    609 void WebGLFramebuffer::drawBuffersIfNecessary(bool force)
    610 {
    611     if (!context()->m_webglDrawBuffers)
    612         return;
    613     bool reset = force;
    614     // This filtering works around graphics driver bugs on Mac OS X.
    615     for (size_t i = 0; i < m_drawBuffers.size(); ++i) {
    616         if (m_drawBuffers[i] != GraphicsContext3D::NONE && getAttachment(m_drawBuffers[i])) {
    617             if (m_filteredDrawBuffers[i] != m_drawBuffers[i]) {
    618                 m_filteredDrawBuffers[i] = m_drawBuffers[i];
    619                 reset = true;
    620             }
    621         } else {
    622             if (m_filteredDrawBuffers[i] != GraphicsContext3D::NONE) {
    623                 m_filteredDrawBuffers[i] = GraphicsContext3D::NONE;
    624                 reset = true;
    625             }
    626         }
    627     }
    628     if (reset) {
    629         context()->graphicsContext3D()->getExtensions()->drawBuffersEXT(
    630             m_filteredDrawBuffers.size(), m_filteredDrawBuffers.data());
    631     }
    632 }
    633 
    634 GC3Denum WebGLFramebuffer::getDrawBuffer(GC3Denum drawBuffer)
    635 {
    636     int index = static_cast<int>(drawBuffer - Extensions3D::DRAW_BUFFER0_EXT);
    637     ASSERT(index >= 0);
    638     if (index < static_cast<int>(m_drawBuffers.size()))
    639         return m_drawBuffers[index];
    640     if (drawBuffer == Extensions3D::DRAW_BUFFER0_EXT)
    641         return GraphicsContext3D::COLOR_ATTACHMENT0;
    642     return GraphicsContext3D::NONE;
    643 }
    644 
    645 }
    646