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#if ENABLE(WEBGL)
29
30#include "WebGLFramebuffer.h"
31
32#include "WebGLRenderingContext.h"
33
34namespace WebCore {
35
36namespace {
37
38    // This function is only for depth/stencil/depth_stencil attachment.
39    // Currently we assume these attachments are all renderbuffers.
40    GC3Denum getInternalFormat(WebGLObject* buffer)
41    {
42        ASSERT(buffer && buffer->isRenderbuffer());
43        return (reinterpret_cast<WebGLRenderbuffer*>(buffer))->getInternalFormat();
44    }
45
46    bool isUninitialized(WebGLObject* attachedObject)
47    {
48        if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()
49            && !(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isInitialized())
50            return true;
51        return false;
52    }
53
54    void setInitialized(WebGLObject* attachedObject)
55    {
56        if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer())
57            (reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->setInitialized();
58    }
59
60    bool isValid(WebGLObject* attachedObject)
61    {
62        if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()) {
63            if (!(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isValid())
64                return false;
65        }
66        return true;
67    }
68
69} // anonymous namespace
70
71PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx)
72{
73    return adoptRef(new WebGLFramebuffer(ctx));
74}
75
76WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx)
77    : WebGLObject(ctx)
78    , m_hasEverBeenBound(false)
79    , m_texTarget(0)
80    , m_texLevel(-1)
81{
82    setObject(context()->graphicsContext3D()->createFramebuffer());
83}
84
85void WebGLFramebuffer::setAttachment(GC3Denum attachment, GC3Denum texTarget, WebGLTexture* texture, GC3Dint level)
86{
87    if (!object())
88        return;
89    if (texture && !texture->object())
90        texture = 0;
91    switch (attachment) {
92    case GraphicsContext3D::COLOR_ATTACHMENT0:
93        m_colorAttachment = texture;
94        if (texture) {
95            m_texTarget = texTarget;
96            m_texLevel = level;
97        }
98        break;
99    case GraphicsContext3D::DEPTH_ATTACHMENT:
100        m_depthAttachment = texture;
101        break;
102    case GraphicsContext3D::STENCIL_ATTACHMENT:
103        m_stencilAttachment = texture;
104        break;
105    case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
106        m_depthStencilAttachment = texture;
107        break;
108    default:
109        return;
110    }
111}
112
113void WebGLFramebuffer::setAttachment(GC3Denum attachment, WebGLRenderbuffer* renderbuffer)
114{
115    if (!object())
116        return;
117    if (renderbuffer && !renderbuffer->object())
118        renderbuffer = 0;
119    switch (attachment) {
120    case GraphicsContext3D::COLOR_ATTACHMENT0:
121        m_colorAttachment = renderbuffer;
122        break;
123    case GraphicsContext3D::DEPTH_ATTACHMENT:
124        m_depthAttachment = renderbuffer;
125        break;
126    case GraphicsContext3D::STENCIL_ATTACHMENT:
127        m_stencilAttachment = renderbuffer;
128        break;
129    case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
130        m_depthStencilAttachment = renderbuffer;
131        break;
132    default:
133        return;
134    }
135}
136
137WebGLObject* WebGLFramebuffer::getAttachment(GC3Denum attachment) const
138{
139    if (!object())
140        return 0;
141    switch (attachment) {
142    case GraphicsContext3D::COLOR_ATTACHMENT0:
143        return m_colorAttachment.get();
144    case GraphicsContext3D::DEPTH_ATTACHMENT:
145        return m_depthAttachment.get();
146    case GraphicsContext3D::STENCIL_ATTACHMENT:
147        return m_stencilAttachment.get();
148    case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
149        return m_depthStencilAttachment.get();
150    default:
151        return 0;
152    }
153}
154
155void WebGLFramebuffer::removeAttachment(WebGLObject* attachment)
156{
157    if (!object())
158        return;
159    if (attachment == m_colorAttachment.get())
160        m_colorAttachment = 0;
161    else if (attachment == m_depthAttachment.get())
162        m_depthAttachment = 0;
163    else if (attachment == m_stencilAttachment.get())
164        m_stencilAttachment = 0;
165    else if (attachment == m_depthStencilAttachment.get())
166        m_depthStencilAttachment = 0;
167    else
168        return;
169}
170
171GC3Dsizei WebGLFramebuffer::getWidth() const
172{
173    if (!object() || !isColorAttached())
174        return 0;
175    if (m_colorAttachment->isRenderbuffer())
176        return (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getWidth();
177    if (m_colorAttachment->isTexture())
178        return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getWidth(m_texTarget, m_texLevel);
179    ASSERT_NOT_REACHED();
180    return 0;
181}
182
183GC3Dsizei WebGLFramebuffer::getHeight() const
184{
185    if (!object() || !isColorAttached())
186        return 0;
187    if (m_colorAttachment->isRenderbuffer())
188        return (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getHeight();
189    if (m_colorAttachment->isTexture())
190        return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getHeight(m_texTarget, m_texLevel);
191    ASSERT_NOT_REACHED();
192    return 0;
193}
194
195GC3Denum WebGLFramebuffer::getColorBufferFormat() const
196{
197    if (!object() || !isColorAttached())
198        return 0;
199    if (m_colorAttachment->isRenderbuffer()) {
200        unsigned long format = (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getInternalFormat();
201        switch (format) {
202        case GraphicsContext3D::RGBA4:
203        case GraphicsContext3D::RGB5_A1:
204            return GraphicsContext3D::RGBA;
205        case GraphicsContext3D::RGB565:
206            return GraphicsContext3D::RGB;
207        }
208        return 0;
209    }
210    if (m_colorAttachment->isTexture())
211        return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getInternalFormat(m_texTarget, m_texLevel);
212    ASSERT_NOT_REACHED();
213    return 0;
214}
215
216bool WebGLFramebuffer::isIncomplete(bool checkInternalFormat) const
217{
218    unsigned int count = 0;
219    if (isDepthAttached()) {
220        if (checkInternalFormat && getInternalFormat(m_depthAttachment.get()) != GraphicsContext3D::DEPTH_COMPONENT16)
221            return true;
222        count++;
223    }
224    if (isStencilAttached()) {
225        if (checkInternalFormat && getInternalFormat(m_stencilAttachment.get()) != GraphicsContext3D::STENCIL_INDEX8)
226            return true;
227        count++;
228    }
229    if (isDepthStencilAttached()) {
230        if (checkInternalFormat && getInternalFormat(m_depthStencilAttachment.get()) != GraphicsContext3D::DEPTH_STENCIL)
231            return true;
232        if (!isValid(m_depthStencilAttachment.get()))
233            return true;
234        count++;
235    }
236    if (count > 1)
237        return true;
238    return false;
239}
240
241bool WebGLFramebuffer::onAccess(bool needToInitializeRenderbuffers)
242{
243    if (isIncomplete(true))
244        return false;
245    if (needToInitializeRenderbuffers)
246        return initializeRenderbuffers();
247    return true;
248}
249
250void WebGLFramebuffer::deleteObjectImpl(Platform3DObject object)
251{
252    context()->graphicsContext3D()->deleteFramebuffer(object);
253    m_colorAttachment = 0;
254    m_depthAttachment = 0;
255    m_stencilAttachment = 0;
256    m_depthStencilAttachment = 0;
257}
258
259bool WebGLFramebuffer::initializeRenderbuffers()
260{
261    ASSERT(object());
262    bool initColor = false, initDepth = false, initStencil = false;
263    GC3Dbitfield mask = 0;
264    if (isUninitialized(m_colorAttachment.get())) {
265        initColor = true;
266        mask |= GraphicsContext3D::COLOR_BUFFER_BIT;
267    }
268    if (isUninitialized(m_depthAttachment.get())) {
269        initDepth = true;
270        mask |= GraphicsContext3D::DEPTH_BUFFER_BIT;
271    }
272    if (isUninitialized(m_stencilAttachment.get())) {
273        initStencil = true;
274        mask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
275    }
276    if (isUninitialized(m_depthStencilAttachment.get())) {
277        initDepth = true;
278        initStencil = true;
279        mask |= (GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT);
280    }
281    if (!initColor && !initDepth && !initStencil)
282        return true;
283
284    // We only clear un-initialized renderbuffers when they are ready to be
285    // read, i.e., when the framebuffer is complete.
286    GraphicsContext3D* g3d = context()->graphicsContext3D();
287    if (g3d->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
288        return false;
289
290    GC3Dfloat colorClearValue[] = {0, 0, 0, 0}, depthClearValue = 0;
291    GC3Dint stencilClearValue = 0;
292    GC3Dboolean colorMask[] = {0, 0, 0, 0}, depthMask = 0;
293    GC3Duint stencilMask = 0xffffffff;
294    GC3Dboolean isScissorEnabled = 0;
295    GC3Dboolean isDitherEnabled = 0;
296    if (initColor) {
297        g3d->getFloatv(GraphicsContext3D::COLOR_CLEAR_VALUE, colorClearValue);
298        g3d->getBooleanv(GraphicsContext3D::COLOR_WRITEMASK, colorMask);
299        g3d->clearColor(0, 0, 0, 0);
300        g3d->colorMask(true, true, true, true);
301    }
302    if (initDepth) {
303        g3d->getFloatv(GraphicsContext3D::DEPTH_CLEAR_VALUE, &depthClearValue);
304        g3d->getBooleanv(GraphicsContext3D::DEPTH_WRITEMASK, &depthMask);
305        g3d->clearDepth(0);
306        g3d->depthMask(true);
307    }
308    if (initStencil) {
309        g3d->getIntegerv(GraphicsContext3D::STENCIL_CLEAR_VALUE, &stencilClearValue);
310        g3d->getIntegerv(GraphicsContext3D::STENCIL_WRITEMASK, reinterpret_cast<GC3Dint*>(&stencilMask));
311        g3d->clearStencil(0);
312        g3d->stencilMask(0xffffffff);
313    }
314    isScissorEnabled = g3d->isEnabled(GraphicsContext3D::SCISSOR_TEST);
315    g3d->disable(GraphicsContext3D::SCISSOR_TEST);
316    isDitherEnabled = g3d->isEnabled(GraphicsContext3D::DITHER);
317    g3d->disable(GraphicsContext3D::DITHER);
318
319    g3d->clear(mask);
320
321    if (initColor) {
322        g3d->clearColor(colorClearValue[0], colorClearValue[1], colorClearValue[2], colorClearValue[3]);
323        g3d->colorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
324    }
325    if (initDepth) {
326        g3d->clearDepth(depthClearValue);
327        g3d->depthMask(depthMask);
328    }
329    if (initStencil) {
330        g3d->clearStencil(stencilClearValue);
331        g3d->stencilMask(stencilMask);
332    }
333    if (isScissorEnabled)
334        g3d->enable(GraphicsContext3D::SCISSOR_TEST);
335    else
336        g3d->disable(GraphicsContext3D::SCISSOR_TEST);
337    if (isDitherEnabled)
338        g3d->enable(GraphicsContext3D::DITHER);
339    else
340        g3d->disable(GraphicsContext3D::DITHER);
341
342    if (initColor)
343        setInitialized(m_colorAttachment.get());
344    if (initDepth && initStencil && m_depthStencilAttachment)
345        setInitialized(m_depthStencilAttachment.get());
346    else {
347        if (initDepth)
348            setInitialized(m_depthAttachment.get());
349        if (initStencil)
350            setInitialized(m_stencilAttachment.get());
351    }
352    return true;
353}
354
355}
356
357#endif // ENABLE(WEBGL)
358