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
34namespace WebCore {
35
36namespace {
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
273WebGLFramebuffer::WebGLAttachment::WebGLAttachment()
274{
275}
276
277WebGLFramebuffer::WebGLAttachment::~WebGLAttachment()
278{
279}
280
281PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx)
282{
283    return adoptRef(new WebGLFramebuffer(ctx));
284}
285
286WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx)
287    : WebGLContextObject(ctx)
288    , m_hasEverBeenBound(false)
289{
290    ScriptWrappable::init(this);
291    setObject(ctx->graphicsContext3D()->createFramebuffer());
292}
293
294WebGLFramebuffer::~WebGLFramebuffer()
295{
296    deleteObject(0);
297}
298
299void 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
312void 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
325void 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
333WebGLSharedObject* 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
341bool 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
427WebGLFramebuffer::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
433void 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
459void 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
483GC3Dsizei 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
494GC3Dsizei 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
505GC3Denum 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
515GC3Denum 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
572bool WebGLFramebuffer::onAccess(GraphicsContext3D* context3d, const char** reason)
573{
574    if (checkStatus(reason) != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
575        return false;
576    return true;
577}
578
579bool 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
587void 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
595bool WebGLFramebuffer::isBound() const
596{
597    return (context()->m_framebufferBinding.get() == this);
598}
599
600void 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
609void 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
634GC3Denum 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