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