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 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 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#if ENABLE(WEBGL)
29
30#include "WebGLFramebuffer.h"
31
32#include "Extensions3D.h"
33#include "WebGLContextGroup.h"
34#include "WebGLDrawBuffers.h"
35#include "WebGLRenderingContext.h"
36
37namespace WebCore {
38
39namespace {
40
41    Platform3DObject objectOrZero(WebGLObject* object)
42    {
43        return object ? object->object() : 0;
44    }
45
46    class WebGLRenderbufferAttachment : public WebGLFramebuffer::WebGLAttachment {
47    public:
48        static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLRenderbuffer*);
49
50    private:
51        WebGLRenderbufferAttachment(WebGLRenderbuffer*);
52        virtual GC3Dsizei getWidth() const override;
53        virtual GC3Dsizei getHeight() const override;
54        virtual GC3Denum getFormat() const override;
55        virtual WebGLSharedObject* getObject() const override;
56        virtual bool isSharedObject(WebGLSharedObject*) const override;
57        virtual bool isValid() const override;
58        virtual bool isInitialized() const override;
59        virtual void setInitialized() override;
60        virtual void onDetached(GraphicsContext3D*) override;
61        virtual void attach(GraphicsContext3D*, GC3Denum attachment) override;
62        virtual void unattach(GraphicsContext3D*, GC3Denum attachment) override;
63
64        WebGLRenderbufferAttachment() { };
65
66        RefPtr<WebGLRenderbuffer> m_renderbuffer;
67    };
68
69    PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLRenderbufferAttachment::create(WebGLRenderbuffer* renderbuffer)
70    {
71        return adoptRef(new WebGLRenderbufferAttachment(renderbuffer));
72    }
73
74    WebGLRenderbufferAttachment::WebGLRenderbufferAttachment(WebGLRenderbuffer* renderbuffer)
75        : m_renderbuffer(renderbuffer)
76    {
77    }
78
79    GC3Dsizei WebGLRenderbufferAttachment::getWidth() const
80    {
81        return m_renderbuffer->getWidth();
82    }
83
84    GC3Dsizei WebGLRenderbufferAttachment::getHeight() const
85    {
86        return m_renderbuffer->getHeight();
87    }
88
89    GC3Denum WebGLRenderbufferAttachment::getFormat() const
90    {
91        return m_renderbuffer->getInternalFormat();
92    }
93
94    WebGLSharedObject* WebGLRenderbufferAttachment::getObject() const
95    {
96        return m_renderbuffer->object() ? m_renderbuffer.get() : 0;
97    }
98
99    bool WebGLRenderbufferAttachment::isSharedObject(WebGLSharedObject* object) const
100    {
101        return object == m_renderbuffer;
102    }
103
104    bool WebGLRenderbufferAttachment::isValid() const
105    {
106        return m_renderbuffer->object();
107    }
108
109    bool WebGLRenderbufferAttachment::isInitialized() const
110    {
111        return m_renderbuffer->object() && m_renderbuffer->isInitialized();
112    }
113
114    void WebGLRenderbufferAttachment::setInitialized()
115    {
116        if (m_renderbuffer->object())
117            m_renderbuffer->setInitialized();
118    }
119
120    void WebGLRenderbufferAttachment::onDetached(GraphicsContext3D* context)
121    {
122        m_renderbuffer->onDetached(context);
123    }
124
125    void WebGLRenderbufferAttachment::attach(GraphicsContext3D* context, GC3Denum attachment)
126    {
127        Platform3DObject object = objectOrZero(m_renderbuffer.get());
128        context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, attachment, GraphicsContext3D::RENDERBUFFER, object);
129    }
130
131    void WebGLRenderbufferAttachment::unattach(GraphicsContext3D* context, GC3Denum attachment)
132    {
133        if (attachment == GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT) {
134            context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0);
135            context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0);
136        } else
137            context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, attachment, GraphicsContext3D::RENDERBUFFER, 0);
138    }
139
140    class WebGLTextureAttachment : public WebGLFramebuffer::WebGLAttachment {
141    public:
142        static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLTexture*, GC3Denum target, GC3Dint level);
143
144    private:
145        WebGLTextureAttachment(WebGLTexture*, GC3Denum target, GC3Dint level);
146        virtual GC3Dsizei getWidth() const override;
147        virtual GC3Dsizei getHeight() const override;
148        virtual GC3Denum getFormat() const override;
149        virtual WebGLSharedObject* getObject() const override;
150        virtual bool isSharedObject(WebGLSharedObject*) const override;
151        virtual bool isValid() const override;
152        virtual bool isInitialized() const override;
153        virtual void setInitialized() override;
154        virtual void onDetached(GraphicsContext3D*) override;
155        virtual void attach(GraphicsContext3D*, GC3Denum attachment) override;
156        virtual void unattach(GraphicsContext3D*, GC3Denum attachment) override;
157
158        WebGLTextureAttachment() { };
159
160        RefPtr<WebGLTexture> m_texture;
161        GC3Denum m_target;
162        GC3Dint m_level;
163    };
164
165    PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLTextureAttachment::create(WebGLTexture* texture, GC3Denum target, GC3Dint level)
166    {
167        return adoptRef(new WebGLTextureAttachment(texture, target, level));
168    }
169
170    WebGLTextureAttachment::WebGLTextureAttachment(WebGLTexture* texture, GC3Denum target, GC3Dint level)
171        : m_texture(texture)
172        , m_target(target)
173        , m_level(level)
174    {
175    }
176
177    GC3Dsizei WebGLTextureAttachment::getWidth() const
178    {
179        return m_texture->getWidth(m_target, m_level);
180    }
181
182    GC3Dsizei WebGLTextureAttachment::getHeight() const
183    {
184        return m_texture->getHeight(m_target, m_level);
185    }
186
187    GC3Denum WebGLTextureAttachment::getFormat() const
188    {
189        return m_texture->getInternalFormat(m_target, m_level);
190    }
191
192    WebGLSharedObject* WebGLTextureAttachment::getObject() const
193    {
194        return m_texture->object() ? m_texture.get() : 0;
195    }
196
197    bool WebGLTextureAttachment::isSharedObject(WebGLSharedObject* object) const
198    {
199        return object == m_texture;
200    }
201
202    bool WebGLTextureAttachment::isValid() const
203    {
204        return m_texture->object();
205    }
206
207    bool WebGLTextureAttachment::isInitialized() const
208    {
209        // Textures are assumed to be initialized.
210        return true;
211    }
212
213    void WebGLTextureAttachment::setInitialized()
214    {
215        // Textures are assumed to be initialized.
216    }
217
218    void WebGLTextureAttachment::onDetached(GraphicsContext3D* context)
219    {
220        m_texture->onDetached(context);
221    }
222
223    void WebGLTextureAttachment::attach(GraphicsContext3D* context, GC3Denum attachment)
224    {
225        Platform3DObject object = objectOrZero(m_texture.get());
226        context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, attachment, m_target, object, m_level);
227    }
228
229    void WebGLTextureAttachment::unattach(GraphicsContext3D* context, GC3Denum attachment)
230    {
231        if (attachment == GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT) {
232            context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, m_target, 0, m_level);
233            context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, m_target, 0, m_level);
234        } else
235            context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, attachment, m_target, 0, m_level);
236    }
237
238    bool isAttachmentComplete(WebGLFramebuffer::WebGLAttachment* attachedObject, GC3Denum attachment, const char** reason)
239    {
240        ASSERT(attachedObject && attachedObject->isValid());
241        ASSERT(reason);
242        GC3Denum format = attachedObject->getFormat();
243        unsigned need = GraphicsContext3D::getClearBitsByAttachmentType(attachment);
244        unsigned have = GraphicsContext3D::getClearBitsByFormat(format);
245
246        if ((need & have) != need) {
247            *reason = "attachment type is not correct for attachment";
248            return false;
249        }
250        if (!attachedObject->getWidth() || !attachedObject->getHeight()) {
251            *reason = "attachment has a 0 dimension";
252            return false;
253        }
254        if ((attachment == GraphicsContext3D::DEPTH_ATTACHMENT || attachment == GraphicsContext3D::STENCIL_ATTACHMENT)
255            && format == GraphicsContext3D::DEPTH_STENCIL) {
256          *reason = "attachment DEPTH_STENCIL not allowed on DEPTH or STENCIL attachment";
257          return false;
258        }
259        return true;
260    }
261
262} // anonymous namespace
263
264WebGLFramebuffer::WebGLAttachment::WebGLAttachment()
265{
266}
267
268WebGLFramebuffer::WebGLAttachment::~WebGLAttachment()
269{
270}
271
272PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx)
273{
274    return adoptRef(new WebGLFramebuffer(ctx));
275}
276
277WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx)
278    : WebGLContextObject(ctx)
279    , m_hasEverBeenBound(false)
280{
281    setObject(ctx->graphicsContext3D()->createFramebuffer());
282}
283
284WebGLFramebuffer::~WebGLFramebuffer()
285{
286    deleteObject(0);
287}
288
289void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, GC3Denum texTarget, WebGLTexture* texture, GC3Dint level)
290{
291    ASSERT(isBound());
292    removeAttachmentFromBoundFramebuffer(attachment);
293    if (!object())
294        return;
295    if (texture && texture->object()) {
296        m_attachments.add(attachment, WebGLTextureAttachment::create(texture, texTarget, level));
297        drawBuffersIfNecessary(false);
298        texture->onAttached();
299    }
300}
301
302void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, WebGLRenderbuffer* renderbuffer)
303{
304    ASSERT(isBound());
305    removeAttachmentFromBoundFramebuffer(attachment);
306    if (!object())
307        return;
308    if (renderbuffer && renderbuffer->object()) {
309        m_attachments.add(attachment, WebGLRenderbufferAttachment::create(renderbuffer));
310        drawBuffersIfNecessary(false);
311        renderbuffer->onAttached();
312    }
313}
314
315void WebGLFramebuffer::attach(GC3Denum attachment, GC3Denum attachmentPoint)
316{
317    ASSERT(isBound());
318    WebGLAttachment* attachmentObject = getAttachment(attachment);
319    if (attachmentObject)
320        attachmentObject->attach(context()->graphicsContext3D(), attachmentPoint);
321}
322
323WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GC3Denum attachment) const
324{
325    if (!object())
326        return 0;
327    WebGLAttachment* attachmentObject = getAttachment(attachment);
328    return attachmentObject ? attachmentObject->getObject() : 0;
329}
330
331WebGLFramebuffer::WebGLAttachment* WebGLFramebuffer::getAttachment(GC3Denum attachment) const
332{
333    const AttachmentMap::const_iterator it = m_attachments.find(attachment);
334    return (it != m_attachments.end()) ? it->value.get() : 0;
335}
336
337void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GC3Denum attachment)
338{
339    ASSERT(isBound());
340    if (!object())
341        return;
342
343    WebGLAttachment* attachmentObject = getAttachment(attachment);
344    if (attachmentObject) {
345        attachmentObject->onDetached(context()->graphicsContext3D());
346        m_attachments.remove(attachment);
347        drawBuffersIfNecessary(false);
348        switch (attachment) {
349        case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
350            attach(GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::DEPTH_ATTACHMENT);
351            attach(GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::STENCIL_ATTACHMENT);
352            break;
353        case GraphicsContext3D::DEPTH_ATTACHMENT:
354            attach(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, GraphicsContext3D::DEPTH_ATTACHMENT);
355            break;
356        case GraphicsContext3D::STENCIL_ATTACHMENT:
357            attach(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, GraphicsContext3D::STENCIL_ATTACHMENT);
358            break;
359        }
360    }
361}
362
363void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLSharedObject* attachment)
364{
365    ASSERT(isBound());
366    if (!object())
367        return;
368    if (!attachment)
369        return;
370
371    bool checkMore = true;
372    while (checkMore) {
373        checkMore = false;
374        for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
375            WebGLAttachment* attachmentObject = it->value.get();
376            if (attachmentObject->isSharedObject(attachment)) {
377                GC3Denum attachmentType = it->key;
378                attachmentObject->unattach(context()->graphicsContext3D(), attachmentType);
379                removeAttachmentFromBoundFramebuffer(attachmentType);
380                checkMore = true;
381                break;
382            }
383        }
384    }
385}
386
387GC3Dsizei WebGLFramebuffer::getColorBufferWidth() const
388{
389    if (!object())
390        return 0;
391    WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0);
392    if (!attachment)
393        return 0;
394
395    return attachment->getWidth();
396}
397
398GC3Dsizei WebGLFramebuffer::getColorBufferHeight() const
399{
400    if (!object())
401        return 0;
402    WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0);
403    if (!attachment)
404        return 0;
405
406    return attachment->getHeight();
407}
408
409GC3Denum WebGLFramebuffer::getColorBufferFormat() const
410{
411    if (!object())
412        return 0;
413    WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0);
414    if (!attachment)
415        return 0;
416    return attachment->getFormat();
417}
418
419GC3Denum WebGLFramebuffer::checkStatus(const char** reason) const
420{
421    unsigned int count = 0;
422    GC3Dsizei width = 0, height = 0;
423    bool haveDepth = false;
424    bool haveStencil = false;
425    bool haveDepthStencil = false;
426    for (AttachmentMap::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
427        WebGLAttachment* attachment = it->value.get();
428        if (!isAttachmentComplete(attachment, it->key, reason))
429            return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
430        if (!attachment->isValid()) {
431            *reason = "attachment is not valid";
432            return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED;
433        }
434        if (!attachment->getFormat()) {
435            *reason = "attachment is an unsupported format";
436            return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
437        }
438        switch (it->key) {
439        case GraphicsContext3D::DEPTH_ATTACHMENT:
440            haveDepth = true;
441            break;
442        case GraphicsContext3D::STENCIL_ATTACHMENT:
443            haveStencil = true;
444            break;
445        case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
446            haveDepthStencil = true;
447            break;
448        }
449        if (!count) {
450            width = attachment->getWidth();
451            height = attachment->getHeight();
452        } else {
453            if (width != attachment->getWidth() || height != attachment->getHeight()) {
454                *reason = "attachments do not have the same dimensions";
455                return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
456            }
457        }
458        ++count;
459    }
460    if (!count) {
461        *reason = "no attachments";
462        return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
463    }
464    if (!width || !height) {
465        *reason = "framebuffer has a 0 dimension";
466        return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
467    }
468    // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
469    if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) {
470        *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments";
471        return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED;
472    }
473    return GraphicsContext3D::FRAMEBUFFER_COMPLETE;
474}
475
476bool WebGLFramebuffer::onAccess(GraphicsContext3D* context3d, bool needToInitializeAttachments, const char** reason)
477{
478    if (checkStatus(reason) != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
479        return false;
480    if (needToInitializeAttachments)
481        return initializeAttachments(context3d, reason);
482    return true;
483}
484
485bool WebGLFramebuffer::hasStencilBuffer() const
486{
487    WebGLAttachment* attachment = getAttachment(GraphicsContext3D::STENCIL_ATTACHMENT);
488    if (!attachment)
489        attachment = getAttachment(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT);
490    return attachment && attachment->isValid();
491}
492
493void WebGLFramebuffer::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object)
494{
495    for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
496        it->value->onDetached(context3d);
497
498    context3d->deleteFramebuffer(object);
499}
500
501bool WebGLFramebuffer::initializeAttachments(GraphicsContext3D* g3d, const char** reason)
502{
503    ASSERT(object());
504    GC3Dbitfield mask = 0;
505
506    for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
507        GC3Denum attachmentType = it->key;
508        WebGLAttachment* attachment = it->value.get();
509        if (!attachment->isInitialized())
510           mask |= GraphicsContext3D::getClearBitsByAttachmentType(attachmentType);
511    }
512    if (!mask)
513        return true;
514
515    // We only clear un-initialized renderbuffers when they are ready to be
516    // read, i.e., when the framebuffer is complete.
517    if (g3d->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
518        *reason = "framebuffer not complete";
519        return false;
520    }
521
522    bool initColor = mask & GraphicsContext3D::COLOR_BUFFER_BIT;
523    bool initDepth = mask & GraphicsContext3D::DEPTH_BUFFER_BIT;
524    bool initStencil = mask & GraphicsContext3D::STENCIL_BUFFER_BIT;
525
526    GC3Dfloat colorClearValue[] = {0, 0, 0, 0}, depthClearValue = 0;
527    GC3Dint stencilClearValue = 0;
528    GC3Dboolean colorMask[] = {0, 0, 0, 0}, depthMask = 0;
529    GC3Duint stencilMask = 0xffffffff;
530    GC3Dboolean isScissorEnabled = 0;
531    GC3Dboolean isDitherEnabled = 0;
532    if (initColor) {
533        g3d->getFloatv(GraphicsContext3D::COLOR_CLEAR_VALUE, colorClearValue);
534        g3d->getBooleanv(GraphicsContext3D::COLOR_WRITEMASK, colorMask);
535        g3d->clearColor(0, 0, 0, 0);
536        g3d->colorMask(true, true, true, true);
537    }
538    if (initDepth) {
539        g3d->getFloatv(GraphicsContext3D::DEPTH_CLEAR_VALUE, &depthClearValue);
540        g3d->getBooleanv(GraphicsContext3D::DEPTH_WRITEMASK, &depthMask);
541        g3d->clearDepth(1.0f);
542        g3d->depthMask(true);
543    }
544    if (initStencil) {
545        g3d->getIntegerv(GraphicsContext3D::STENCIL_CLEAR_VALUE, &stencilClearValue);
546        g3d->getIntegerv(GraphicsContext3D::STENCIL_WRITEMASK, reinterpret_cast<GC3Dint*>(&stencilMask));
547        g3d->clearStencil(0);
548        g3d->stencilMask(0xffffffff);
549    }
550    isScissorEnabled = g3d->isEnabled(GraphicsContext3D::SCISSOR_TEST);
551    g3d->disable(GraphicsContext3D::SCISSOR_TEST);
552    isDitherEnabled = g3d->isEnabled(GraphicsContext3D::DITHER);
553    g3d->disable(GraphicsContext3D::DITHER);
554
555    g3d->clear(mask);
556
557    if (initColor) {
558        g3d->clearColor(colorClearValue[0], colorClearValue[1], colorClearValue[2], colorClearValue[3]);
559        g3d->colorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
560    }
561    if (initDepth) {
562        g3d->clearDepth(depthClearValue);
563        g3d->depthMask(depthMask);
564    }
565    if (initStencil) {
566        g3d->clearStencil(stencilClearValue);
567        g3d->stencilMask(stencilMask);
568    }
569    if (isScissorEnabled)
570        g3d->enable(GraphicsContext3D::SCISSOR_TEST);
571    else
572        g3d->disable(GraphicsContext3D::SCISSOR_TEST);
573    if (isDitherEnabled)
574        g3d->enable(GraphicsContext3D::DITHER);
575    else
576        g3d->disable(GraphicsContext3D::DITHER);
577
578    for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
579        GC3Denum attachmentType = it->key;
580        WebGLAttachment* attachment = it->value.get();
581        GC3Dbitfield bits = GraphicsContext3D::getClearBitsByAttachmentType(attachmentType);
582        if (bits & mask)
583            attachment->setInitialized();
584    }
585    return true;
586}
587
588bool WebGLFramebuffer::isBound() const
589{
590    return (context()->m_framebufferBinding.get() == this);
591}
592
593void WebGLFramebuffer::drawBuffers(const Vector<GC3Denum>& bufs)
594{
595    m_drawBuffers = bufs;
596    m_filteredDrawBuffers.resize(m_drawBuffers.size());
597    for (size_t i = 0; i < m_filteredDrawBuffers.size(); ++i)
598        m_filteredDrawBuffers[i] = GraphicsContext3D::NONE;
599    drawBuffersIfNecessary(true);
600}
601
602void WebGLFramebuffer::drawBuffersIfNecessary(bool force)
603{
604    if (!context()->m_webglDrawBuffers)
605        return;
606    bool reset = force;
607    // This filtering works around graphics driver bugs on Mac OS X.
608    for (size_t i = 0; i < m_drawBuffers.size(); ++i) {
609        if (m_drawBuffers[i] != GraphicsContext3D::NONE && getAttachment(m_drawBuffers[i])) {
610            if (m_filteredDrawBuffers[i] != m_drawBuffers[i]) {
611                m_filteredDrawBuffers[i] = m_drawBuffers[i];
612                reset = true;
613            }
614        } else {
615            if (m_filteredDrawBuffers[i] != GraphicsContext3D::NONE) {
616                m_filteredDrawBuffers[i] = GraphicsContext3D::NONE;
617                reset = true;
618            }
619        }
620    }
621    if (reset) {
622        context()->graphicsContext3D()->getExtensions()->drawBuffersEXT(
623            m_filteredDrawBuffers.size(), m_filteredDrawBuffers.data());
624    }
625}
626
627GC3Denum WebGLFramebuffer::getDrawBuffer(GC3Denum drawBuffer)
628{
629    int index = static_cast<int>(drawBuffer - Extensions3D::DRAW_BUFFER0_EXT);
630    ASSERT(index >= 0);
631    if (index < static_cast<int>(m_drawBuffers.size()))
632        return m_drawBuffers[index];
633    if (drawBuffer == Extensions3D::DRAW_BUFFER0_EXT)
634        return GraphicsContext3D::COLOR_ATTACHMENT0;
635    return GraphicsContext3D::NONE;
636}
637
638}
639
640#endif // ENABLE(WEBGL)
641