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/WebGLRenderingContextBase.h"
31#include "platform/NotImplemented.h"
32
33namespace blink {
34
35namespace {
36
37    Platform3DObject objectOrZero(WebGLObject* object)
38    {
39        return object ? object->object() : 0;
40    }
41
42    class WebGLRenderbufferAttachment FINAL : public WebGLFramebuffer::WebGLAttachment {
43    public:
44        static PassRefPtrWillBeRawPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLRenderbuffer*);
45
46        virtual void trace(Visitor*) OVERRIDE;
47
48    private:
49        explicit WebGLRenderbufferAttachment(WebGLRenderbuffer*);
50        WebGLRenderbufferAttachment() { }
51
52        virtual GLsizei width() const OVERRIDE;
53        virtual GLsizei height() const OVERRIDE;
54        virtual GLenum format() const OVERRIDE;
55        virtual GLenum type() const OVERRIDE;
56        virtual WebGLSharedObject* object() const OVERRIDE;
57        virtual bool isSharedObject(WebGLSharedObject*) const OVERRIDE;
58        virtual bool valid() const OVERRIDE;
59        virtual void onDetached(blink::WebGraphicsContext3D*) OVERRIDE;
60        virtual void attach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
61        virtual void unattach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
62
63        RefPtrWillBeMember<WebGLRenderbuffer> m_renderbuffer;
64    };
65
66    PassRefPtrWillBeRawPtr<WebGLFramebuffer::WebGLAttachment> WebGLRenderbufferAttachment::create(WebGLRenderbuffer* renderbuffer)
67    {
68        return adoptRefWillBeNoop(new WebGLRenderbufferAttachment(renderbuffer));
69    }
70
71    void WebGLRenderbufferAttachment::trace(Visitor* visitor)
72    {
73        visitor->trace(m_renderbuffer);
74        WebGLFramebuffer::WebGLAttachment::trace(visitor);
75    }
76
77    WebGLRenderbufferAttachment::WebGLRenderbufferAttachment(WebGLRenderbuffer* renderbuffer)
78        : m_renderbuffer(renderbuffer)
79    {
80    }
81
82    GLsizei WebGLRenderbufferAttachment::width() const
83    {
84        return m_renderbuffer->width();
85    }
86
87    GLsizei WebGLRenderbufferAttachment::height() const
88    {
89        return m_renderbuffer->height();
90    }
91
92    GLenum WebGLRenderbufferAttachment::format() const
93    {
94        GLenum format = m_renderbuffer->internalFormat();
95        if (format == GL_DEPTH_STENCIL_OES
96            && m_renderbuffer->emulatedStencilBuffer()
97            && m_renderbuffer->emulatedStencilBuffer()->internalFormat() != GL_STENCIL_INDEX8) {
98            return 0;
99        }
100        return format;
101    }
102
103    WebGLSharedObject* WebGLRenderbufferAttachment::object() const
104    {
105        return m_renderbuffer->object() ? m_renderbuffer.get() : 0;
106    }
107
108    bool WebGLRenderbufferAttachment::isSharedObject(WebGLSharedObject* object) const
109    {
110        return object == m_renderbuffer;
111    }
112
113    bool WebGLRenderbufferAttachment::valid() const
114    {
115        return m_renderbuffer->object();
116    }
117
118    void WebGLRenderbufferAttachment::onDetached(blink::WebGraphicsContext3D* context)
119    {
120        m_renderbuffer->onDetached(context);
121    }
122
123    void WebGLRenderbufferAttachment::attach(blink::WebGraphicsContext3D* context, GLenum attachment)
124    {
125        Platform3DObject object = objectOrZero(m_renderbuffer.get());
126        if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL && m_renderbuffer->emulatedStencilBuffer()) {
127            context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, object);
128            context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, objectOrZero(m_renderbuffer->emulatedStencilBuffer()));
129        } else {
130            context->framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, object);
131        }
132    }
133
134    void WebGLRenderbufferAttachment::unattach(blink::WebGraphicsContext3D* context, GLenum attachment)
135    {
136        if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
137            context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
138            context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
139        } else {
140            context->framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, 0);
141        }
142    }
143
144    GLenum WebGLRenderbufferAttachment::type() const
145    {
146        notImplemented();
147        return 0;
148    }
149
150    class WebGLTextureAttachment FINAL : public WebGLFramebuffer::WebGLAttachment {
151    public:
152        static PassRefPtrWillBeRawPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLTexture*, GLenum target, GLint level);
153
154        virtual void trace(Visitor*) OVERRIDE;
155
156    private:
157        WebGLTextureAttachment(WebGLTexture*, GLenum target, GLint level);
158        WebGLTextureAttachment() { }
159
160        virtual GLsizei width() const OVERRIDE;
161        virtual GLsizei height() const OVERRIDE;
162        virtual GLenum format() const OVERRIDE;
163        virtual GLenum type() const OVERRIDE;
164        virtual WebGLSharedObject* object() const OVERRIDE;
165        virtual bool isSharedObject(WebGLSharedObject*) const OVERRIDE;
166        virtual bool valid() const OVERRIDE;
167        virtual void onDetached(blink::WebGraphicsContext3D*) OVERRIDE;
168        virtual void attach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
169        virtual void unattach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
170
171        RefPtrWillBeMember<WebGLTexture> m_texture;
172        GLenum m_target;
173        GLint m_level;
174    };
175
176    PassRefPtrWillBeRawPtr<WebGLFramebuffer::WebGLAttachment> WebGLTextureAttachment::create(WebGLTexture* texture, GLenum target, GLint level)
177    {
178        return adoptRefWillBeNoop(new WebGLTextureAttachment(texture, target, level));
179    }
180
181    void WebGLTextureAttachment::trace(Visitor* visitor)
182    {
183        visitor->trace(m_texture);
184        WebGLFramebuffer::WebGLAttachment::trace(visitor);
185    }
186
187    WebGLTextureAttachment::WebGLTextureAttachment(WebGLTexture* texture, GLenum target, GLint level)
188        : m_texture(texture)
189        , m_target(target)
190        , m_level(level)
191    {
192    }
193
194    GLsizei WebGLTextureAttachment::width() const
195    {
196        return m_texture->getWidth(m_target, m_level);
197    }
198
199    GLsizei WebGLTextureAttachment::height() const
200    {
201        return m_texture->getHeight(m_target, m_level);
202    }
203
204    GLenum WebGLTextureAttachment::format() const
205    {
206        return m_texture->getInternalFormat(m_target, m_level);
207    }
208
209    WebGLSharedObject* WebGLTextureAttachment::object() const
210    {
211        return m_texture->object() ? m_texture.get() : 0;
212    }
213
214    bool WebGLTextureAttachment::isSharedObject(WebGLSharedObject* object) const
215    {
216        return object == m_texture;
217    }
218
219    bool WebGLTextureAttachment::valid() const
220    {
221        return m_texture->object();
222    }
223
224    void WebGLTextureAttachment::onDetached(blink::WebGraphicsContext3D* context)
225    {
226        m_texture->onDetached(context);
227    }
228
229    void WebGLTextureAttachment::attach(blink::WebGraphicsContext3D* context, GLenum attachment)
230    {
231        Platform3DObject object = objectOrZero(m_texture.get());
232        context->framebufferTexture2D(GL_FRAMEBUFFER, attachment, m_target, object, m_level);
233    }
234
235    void WebGLTextureAttachment::unattach(blink::WebGraphicsContext3D* context, GLenum attachment)
236    {
237        if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
238            context->framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_target, 0, m_level);
239            context->framebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, m_target, 0, m_level);
240        } else {
241            context->framebufferTexture2D(GL_FRAMEBUFFER, attachment, m_target, 0, m_level);
242        }
243    }
244
245    GLenum WebGLTextureAttachment::type() const
246    {
247        return m_texture->getType(m_target, m_level);
248    }
249
250    bool isColorRenderable(GLenum internalformat)
251    {
252        switch (internalformat) {
253        case GL_RGBA4:
254        case GL_RGB5_A1:
255        case GL_RGB565:
256            return true;
257        default:
258            return false;
259        }
260    }
261
262} // anonymous namespace
263
264WebGLFramebuffer::WebGLAttachment::WebGLAttachment()
265{
266}
267
268WebGLFramebuffer::WebGLAttachment::~WebGLAttachment()
269{
270}
271
272PassRefPtrWillBeRawPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContextBase* ctx)
273{
274    return adoptRefWillBeNoop(new WebGLFramebuffer(ctx));
275}
276
277WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContextBase* ctx)
278    : WebGLContextObject(ctx)
279    , m_hasEverBeenBound(false)
280{
281    setObject(ctx->webContext()->createFramebuffer());
282}
283
284WebGLFramebuffer::~WebGLFramebuffer()
285{
286    // Delete the platform framebuffer resource. Explicit detachment
287    // is for the benefit of Oilpan, where the framebuffer object
288    // isn't detached when it and the WebGLRenderingContextBase object
289    // it is registered with are both finalized. Without Oilpan, the
290    // object will have been detached.
291    //
292    // To keep the code regular, the trivial detach()ment is always
293    // performed.
294    detachAndDeleteObject();
295}
296
297void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GLenum attachment, GLenum texTarget, WebGLTexture* texture, GLint level)
298{
299    ASSERT(isBound());
300    removeAttachmentFromBoundFramebuffer(attachment);
301    if (!object())
302        return;
303    if (texture && texture->object()) {
304        m_attachments.add(attachment, WebGLTextureAttachment::create(texture, texTarget, level));
305        drawBuffersIfNecessary(false);
306        texture->onAttached();
307    }
308}
309
310void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GLenum attachment, WebGLRenderbuffer* renderbuffer)
311{
312    ASSERT(isBound());
313    removeAttachmentFromBoundFramebuffer(attachment);
314    if (!object())
315        return;
316    if (renderbuffer && renderbuffer->object()) {
317        m_attachments.add(attachment, WebGLRenderbufferAttachment::create(renderbuffer));
318        drawBuffersIfNecessary(false);
319        renderbuffer->onAttached();
320    }
321}
322
323void WebGLFramebuffer::attach(GLenum attachment, GLenum attachmentPoint)
324{
325    ASSERT(isBound());
326    WebGLAttachment* attachmentObject = getAttachment(attachment);
327    if (attachmentObject)
328        attachmentObject->attach(context()->webContext(), attachmentPoint);
329}
330
331WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GLenum attachment) const
332{
333    if (!object())
334        return 0;
335    WebGLAttachment* attachmentObject = getAttachment(attachment);
336    return attachmentObject ? attachmentObject->object() : 0;
337}
338
339bool WebGLFramebuffer::isAttachmentComplete(WebGLAttachment* attachedObject, GLenum attachment, const char** reason) const
340{
341    ASSERT(attachedObject && attachedObject->valid());
342    ASSERT(reason);
343
344    GLenum internalformat = attachedObject->format();
345    WebGLSharedObject* object = attachedObject->object();
346    ASSERT(object && (object->isTexture() || object->isRenderbuffer()));
347
348    if (attachment == GL_DEPTH_ATTACHMENT) {
349        if (object->isRenderbuffer()) {
350            if (internalformat != GL_DEPTH_COMPONENT16) {
351                *reason = "the internalformat of the attached renderbuffer is not DEPTH_COMPONENT16";
352                return false;
353            }
354        } else if (object->isTexture()) {
355            GLenum type = attachedObject->type();
356            if (!(context()->extensionEnabled(WebGLDepthTextureName) && internalformat == GL_DEPTH_COMPONENT
357                && (type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT))) {
358                *reason = "the attached texture is not a depth texture";
359                return false;
360            }
361        }
362    } else if (attachment == GL_STENCIL_ATTACHMENT) {
363        // Depend on the underlying GL drivers to check stencil textures
364        // and check renderbuffer type here only.
365        if (object->isRenderbuffer()) {
366            if (internalformat != GL_STENCIL_INDEX8) {
367                *reason = "the internalformat of the attached renderbuffer is not STENCIL_INDEX8";
368                return false;
369            }
370        }
371    } else if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
372        if (object->isRenderbuffer()) {
373            if (internalformat != GL_DEPTH_STENCIL_OES) {
374                *reason = "the internalformat of the attached renderbuffer is not DEPTH_STENCIL";
375                return false;
376            }
377        } else if (object->isTexture()) {
378            GLenum type = attachedObject->type();
379            if (!(context()->extensionEnabled(WebGLDepthTextureName) && internalformat == GL_DEPTH_STENCIL_OES
380                && type == GL_UNSIGNED_INT_24_8_OES)) {
381                *reason = "the attached texture is not a DEPTH_STENCIL texture";
382                return false;
383            }
384        }
385    } else if (attachment == GL_COLOR_ATTACHMENT0
386        || (context()->extensionEnabled(WebGLDrawBuffersName) && attachment > GL_COLOR_ATTACHMENT0
387            && attachment < static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + context()->maxColorAttachments()))) {
388        if (object->isRenderbuffer()) {
389            if (!isColorRenderable(internalformat)) {
390                *reason = "the internalformat of the attached renderbuffer is not color-renderable";
391                return false;
392            }
393        } else if (object->isTexture()) {
394            GLenum type = attachedObject->type();
395            if (internalformat != GL_RGBA && internalformat != GL_RGB) {
396                *reason = "the internalformat of the attached texture is not color-renderable";
397                return false;
398            }
399            // TODO: WEBGL_color_buffer_float and EXT_color_buffer_half_float extensions have not been implemented in
400            // WebGL yet. It would be better to depend on the underlying GL drivers to check on rendering to floating point textures
401            // and add the check back to WebGL when above two extensions are implemented.
402            // Assume UNSIGNED_BYTE is renderable here without the need to explicitly check if GL_OES_rgb8_rgba8 extension is supported.
403            if (type != GL_UNSIGNED_BYTE
404                && type != GL_UNSIGNED_SHORT_5_6_5
405                && type != GL_UNSIGNED_SHORT_4_4_4_4
406                && type != GL_UNSIGNED_SHORT_5_5_5_1
407                && !(type == GL_FLOAT && context()->extensionEnabled(OESTextureFloatName))
408                && !(type == GL_HALF_FLOAT_OES && context()->extensionEnabled(OESTextureHalfFloatName))) {
409                *reason = "unsupported type: The attached texture is not supported to be rendered to";
410                return false;
411            }
412        }
413    } else {
414        *reason = "unknown framebuffer attachment point";
415        return false;
416    }
417
418    if (!attachedObject->width() || !attachedObject->height()) {
419        *reason = "attachment has a 0 dimension";
420        return false;
421    }
422    return true;
423}
424
425WebGLFramebuffer::WebGLAttachment* WebGLFramebuffer::getAttachment(GLenum attachment) const
426{
427    const AttachmentMap::const_iterator it = m_attachments.find(attachment);
428    return (it != m_attachments.end()) ? it->value.get() : 0;
429}
430
431void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GLenum attachment)
432{
433    ASSERT(isBound());
434    if (!object())
435        return;
436
437    WebGLAttachment* attachmentObject = getAttachment(attachment);
438    if (attachmentObject) {
439        attachmentObject->onDetached(context()->webContext());
440        m_attachments.remove(attachment);
441        drawBuffersIfNecessary(false);
442        switch (attachment) {
443        case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL:
444            attach(GL_DEPTH_ATTACHMENT, GL_DEPTH_ATTACHMENT);
445            attach(GL_STENCIL_ATTACHMENT, GL_STENCIL_ATTACHMENT);
446            break;
447        case GL_DEPTH_ATTACHMENT:
448            attach(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL, GL_DEPTH_ATTACHMENT);
449            break;
450        case GL_STENCIL_ATTACHMENT:
451            attach(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL, GL_STENCIL_ATTACHMENT);
452            break;
453        }
454    }
455}
456
457void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLSharedObject* attachment)
458{
459    ASSERT(isBound());
460    if (!object())
461        return;
462    if (!attachment)
463        return;
464
465    bool checkMore = true;
466    while (checkMore) {
467        checkMore = false;
468        for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
469            WebGLAttachment* attachmentObject = it->value.get();
470            if (attachmentObject->isSharedObject(attachment)) {
471                GLenum attachmentType = it->key;
472                attachmentObject->unattach(context()->webContext(), attachmentType);
473                removeAttachmentFromBoundFramebuffer(attachmentType);
474                checkMore = true;
475                break;
476            }
477        }
478    }
479}
480
481GLenum WebGLFramebuffer::colorBufferFormat() const
482{
483    if (!object())
484        return 0;
485    WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0);
486    if (!attachment)
487        return 0;
488    return attachment->format();
489}
490
491GLenum WebGLFramebuffer::checkStatus(const char** reason) const
492{
493    unsigned count = 0;
494    GLsizei width = 0, height = 0;
495    bool haveDepth = false;
496    bool haveStencil = false;
497    bool haveDepthStencil = false;
498    for (AttachmentMap::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
499        WebGLAttachment* attachment = it->value.get();
500        if (!isAttachmentComplete(attachment, it->key, reason))
501            return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
502        if (!attachment->valid()) {
503            *reason = "attachment is not valid";
504            return GL_FRAMEBUFFER_UNSUPPORTED;
505        }
506        if (!attachment->format()) {
507            *reason = "attachment is an unsupported format";
508            return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
509        }
510        switch (it->key) {
511        case GL_DEPTH_ATTACHMENT:
512            haveDepth = true;
513            break;
514        case GL_STENCIL_ATTACHMENT:
515            haveStencil = true;
516            break;
517        case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL:
518            haveDepthStencil = true;
519            break;
520        }
521        if (!count) {
522            width = attachment->width();
523            height = attachment->height();
524        } else {
525            if (width != attachment->width() || height != attachment->height()) {
526                *reason = "attachments do not have the same dimensions";
527                return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
528            }
529        }
530        ++count;
531    }
532    if (!count) {
533        *reason = "no attachments";
534        return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
535    }
536    if (!width || !height) {
537        *reason = "framebuffer has a 0 dimension";
538        return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
539    }
540    // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
541    if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) {
542        *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments";
543        return GL_FRAMEBUFFER_UNSUPPORTED;
544    }
545    return GL_FRAMEBUFFER_COMPLETE;
546}
547
548bool WebGLFramebuffer::onAccess(blink::WebGraphicsContext3D* context3d, const char** reason)
549{
550    if (checkStatus(reason) != GL_FRAMEBUFFER_COMPLETE)
551        return false;
552    return true;
553}
554
555bool WebGLFramebuffer::hasStencilBuffer() const
556{
557    WebGLAttachment* attachment = getAttachment(GL_STENCIL_ATTACHMENT);
558    if (!attachment)
559        attachment = getAttachment(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL);
560    return attachment && attachment->valid();
561}
562
563void WebGLFramebuffer::deleteObjectImpl(blink::WebGraphicsContext3D* context3d, Platform3DObject object)
564{
565#if !ENABLE(OILPAN)
566    // With Oilpan, both the AttachmentMap and its WebGLAttachment objects are
567    // GCed objects and cannot be accessed, as they may have been finalized
568    // already during the same GC sweep.
569    //
570    // The WebGLAttachment-derived classes instead handle detachment
571    // on their own when finalizing, so the explicit notification is
572    // not needed.
573    for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
574        it->value->onDetached(context3d);
575#endif
576
577    context3d->deleteFramebuffer(object);
578}
579
580bool WebGLFramebuffer::isBound() const
581{
582    return (context()->m_framebufferBinding.get() == this);
583}
584
585void WebGLFramebuffer::drawBuffers(const Vector<GLenum>& bufs)
586{
587    m_drawBuffers = bufs;
588    m_filteredDrawBuffers.resize(m_drawBuffers.size());
589    for (size_t i = 0; i < m_filteredDrawBuffers.size(); ++i)
590        m_filteredDrawBuffers[i] = GL_NONE;
591    drawBuffersIfNecessary(true);
592}
593
594void WebGLFramebuffer::drawBuffersIfNecessary(bool force)
595{
596    if (!context()->extensionEnabled(WebGLDrawBuffersName))
597        return;
598    bool reset = force;
599    // This filtering works around graphics driver bugs on Mac OS X.
600    for (size_t i = 0; i < m_drawBuffers.size(); ++i) {
601        if (m_drawBuffers[i] != GL_NONE && getAttachment(m_drawBuffers[i])) {
602            if (m_filteredDrawBuffers[i] != m_drawBuffers[i]) {
603                m_filteredDrawBuffers[i] = m_drawBuffers[i];
604                reset = true;
605            }
606        } else {
607            if (m_filteredDrawBuffers[i] != GL_NONE) {
608                m_filteredDrawBuffers[i] = GL_NONE;
609                reset = true;
610            }
611        }
612    }
613    if (reset) {
614        context()->webContext()->drawBuffersEXT(
615            m_filteredDrawBuffers.size(), m_filteredDrawBuffers.data());
616    }
617}
618
619GLenum WebGLFramebuffer::getDrawBuffer(GLenum drawBuffer)
620{
621    int index = static_cast<int>(drawBuffer - GL_DRAW_BUFFER0_EXT);
622    ASSERT(index >= 0);
623    if (index < static_cast<int>(m_drawBuffers.size()))
624        return m_drawBuffers[index];
625    if (drawBuffer == GL_DRAW_BUFFER0_EXT)
626        return GL_COLOR_ATTACHMENT0;
627    return GL_NONE;
628}
629
630void WebGLFramebuffer::trace(Visitor* visitor)
631{
632#if ENABLE(OILPAN)
633    visitor->trace(m_attachments);
634#endif
635    WebGLContextObject::trace(visitor);
636}
637
638}
639