d1af518c051655f9341d2dfbbac169c631b562bf
[WebKit-https.git] / Source / WebCore / html / canvas / WebGLFramebuffer.cpp
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
34 namespace WebCore {
35
36 namespace {
37
38     bool isAttachmentComplete(WebGLObject* attachedObject, GC3Denum attachment)
39     {
40         ASSERT(attachedObject && attachedObject->object());
41         ASSERT(attachedObject->isRenderbuffer());
42         WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject);
43         switch (attachment) {
44         case GraphicsContext3D::DEPTH_ATTACHMENT:
45             if (buffer->getInternalFormat() != GraphicsContext3D::DEPTH_COMPONENT16)
46                 return false;
47             break;
48         case GraphicsContext3D::STENCIL_ATTACHMENT:
49             if (buffer->getInternalFormat() != GraphicsContext3D::STENCIL_INDEX8)
50                 return false;
51             break;
52         case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
53             if (buffer->getInternalFormat() != GraphicsContext3D::DEPTH_STENCIL)
54                 return false;
55             break;
56         default:
57             ASSERT_NOT_REACHED();
58             return false;
59         }
60         if (!buffer->getWidth() || !buffer->getHeight())
61             return false;
62         return true;
63     }
64
65     GC3Dsizei getImageWidth(WebGLObject* attachedObject)
66     {
67         ASSERT(attachedObject && attachedObject->object());
68         ASSERT(attachedObject->isRenderbuffer());
69         WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject);
70         return buffer->getWidth();
71     }
72
73     GC3Dsizei getImageHeight(WebGLObject* attachedObject)
74     {
75         ASSERT(attachedObject && attachedObject->object());
76         ASSERT(attachedObject->isRenderbuffer());
77         WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject);
78         return buffer->getHeight();
79     }
80
81     bool isUninitialized(WebGLObject* attachedObject)
82     {
83         if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()
84             && !(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isInitialized())
85             return true;
86         return false;
87     }
88
89     void setInitialized(WebGLObject* attachedObject)
90     {
91         if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer())
92             (reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->setInitialized();
93     }
94
95     bool isValid(WebGLObject* attachedObject)
96     {
97         if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()) {
98             if (!(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isValid())
99                 return false;
100         }
101         return true;
102     }
103
104 } // anonymous namespace
105
106 PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx)
107 {
108     return adoptRef(new WebGLFramebuffer(ctx));
109 }
110
111 WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx)
112     : WebGLObject(ctx)
113     , m_hasEverBeenBound(false)
114     , m_texTarget(0)
115     , m_texLevel(-1)
116 {
117     setObject(context()->graphicsContext3D()->createFramebuffer());
118 }
119
120 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, GC3Denum texTarget, WebGLTexture* texture, GC3Dint level)
121 {
122     ASSERT(isBound());
123     if (!object())
124         return;
125     removeAttachmentFromBoundFramebuffer(attachment);
126     if (texture && !texture->object())
127         texture = 0;
128     switch (attachment) {
129     case GraphicsContext3D::COLOR_ATTACHMENT0:
130         m_colorAttachment = texture;
131         if (texture) {
132             m_texTarget = texTarget;
133             m_texLevel = level;
134         }
135         break;
136     case GraphicsContext3D::DEPTH_ATTACHMENT:
137         m_depthAttachment = texture;
138         break;
139     case GraphicsContext3D::STENCIL_ATTACHMENT:
140         m_stencilAttachment = texture;
141         break;
142     case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
143         m_depthStencilAttachment = texture;
144         break;
145     default:
146         ASSERT_NOT_REACHED();
147         break;
148     }
149     if (texture)
150         texture->onAttached();
151 }
152
153 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, WebGLRenderbuffer* renderbuffer)
154 {
155     ASSERT(isBound());
156     if (!object())
157         return;
158     removeAttachmentFromBoundFramebuffer(attachment);
159     if (renderbuffer && !renderbuffer->object())
160         renderbuffer = 0;
161     switch (attachment) {
162     case GraphicsContext3D::COLOR_ATTACHMENT0:
163         m_colorAttachment = renderbuffer;
164         break;
165     case GraphicsContext3D::DEPTH_ATTACHMENT:
166         m_depthAttachment = renderbuffer;
167         break;
168     case GraphicsContext3D::STENCIL_ATTACHMENT:
169         m_stencilAttachment = renderbuffer;
170         break;
171     case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
172         m_depthStencilAttachment = renderbuffer;
173         break;
174     default:
175         ASSERT_NOT_REACHED();
176         break;
177     }
178     if (renderbuffer)
179         renderbuffer->onAttached();
180 }
181
182 WebGLObject* WebGLFramebuffer::getAttachment(GC3Denum attachment) const
183 {
184     if (!object())
185         return 0;
186     switch (attachment) {
187     case GraphicsContext3D::COLOR_ATTACHMENT0:
188         return m_colorAttachment.get();
189     case GraphicsContext3D::DEPTH_ATTACHMENT:
190         return m_depthAttachment.get();
191     case GraphicsContext3D::STENCIL_ATTACHMENT:
192         return m_stencilAttachment.get();
193     case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
194         return m_depthStencilAttachment.get();
195     default:
196         ASSERT_NOT_REACHED();
197         return 0;
198     }
199 }
200
201 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GC3Denum attachment)
202 {
203     ASSERT(isBound());
204     if (!object())
205         return;
206     switch (attachment) {
207     case GraphicsContext3D::COLOR_ATTACHMENT0:
208         if (m_colorAttachment) {
209             m_colorAttachment->onDetached();
210             m_colorAttachment = 0;
211             m_texTarget = 0;
212             m_texLevel = -1;
213         }
214         break;
215     case GraphicsContext3D::DEPTH_ATTACHMENT:
216         if (m_depthAttachment) {
217             m_depthAttachment->onDetached();
218             m_depthAttachment = 0;
219         }
220         break;
221     case GraphicsContext3D::STENCIL_ATTACHMENT:
222         if (m_stencilAttachment) {
223             m_stencilAttachment->onDetached();
224             m_stencilAttachment = 0;
225         }
226         break;
227     case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
228         if (m_depthStencilAttachment) {
229             m_depthStencilAttachment->onDetached();
230             m_depthStencilAttachment = 0;
231         }
232         break;
233     default:
234         ASSERT_NOT_REACHED();
235         break;
236     }
237 }
238
239 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLObject* attachment)
240 {
241     ASSERT(isBound());
242     if (!object())
243         return;
244     if (!attachment)
245         return;
246     GraphicsContext3D* gc3d = context()->graphicsContext3D();
247     if (attachment == m_colorAttachment.get()) {
248         if (attachment->isRenderbuffer())
249             gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, 0);
250         else
251             gc3d->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, m_texTarget, 0, m_texLevel);
252         removeAttachmentFromBoundFramebuffer(GraphicsContext3D::COLOR_ATTACHMENT0);
253     }
254     if (attachment == m_depthAttachment.get()) {
255         gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0);
256         removeAttachmentFromBoundFramebuffer(GraphicsContext3D::DEPTH_ATTACHMENT);
257     }
258     if (attachment == m_stencilAttachment.get()) {
259         gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0);
260         removeAttachmentFromBoundFramebuffer(GraphicsContext3D::STENCIL_ATTACHMENT);
261     }
262     if (attachment == m_depthStencilAttachment.get()) {
263         gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0);
264         gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0);
265         removeAttachmentFromBoundFramebuffer(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT);
266     }
267 }
268
269 GC3Dsizei WebGLFramebuffer::getColorBufferWidth() const
270 {
271     if (!object() || !isColorAttached())
272         return 0;
273     if (m_colorAttachment->isRenderbuffer())
274         return (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getWidth();
275     if (m_colorAttachment->isTexture())
276         return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getWidth(m_texTarget, m_texLevel);
277     ASSERT_NOT_REACHED();
278     return 0;
279 }
280
281 GC3Dsizei WebGLFramebuffer::getColorBufferHeight() const
282 {
283     if (!object() || !isColorAttached())
284         return 0;
285     if (m_colorAttachment->isRenderbuffer())
286         return (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getHeight();
287     if (m_colorAttachment->isTexture())
288         return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getHeight(m_texTarget, m_texLevel);
289     ASSERT_NOT_REACHED();
290     return 0;
291 }
292
293 GC3Denum WebGLFramebuffer::getColorBufferFormat() const
294 {
295     if (!object() || !isColorAttached())
296         return 0;
297     if (m_colorAttachment->isRenderbuffer()) {
298         unsigned long format = (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getInternalFormat();
299         switch (format) {
300         case GraphicsContext3D::RGBA4:
301         case GraphicsContext3D::RGB5_A1:
302             return GraphicsContext3D::RGBA;
303         case GraphicsContext3D::RGB565:
304             return GraphicsContext3D::RGB;
305         }
306         return 0;
307     }
308     if (m_colorAttachment->isTexture())
309         return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getInternalFormat(m_texTarget, m_texLevel);
310     ASSERT_NOT_REACHED();
311     return 0;
312 }
313
314 GC3Denum WebGLFramebuffer::checkStatus() const
315 {
316     unsigned int count = 0;
317     GC3Dsizei width = 0, height = 0;
318     if (isDepthAttached()) {
319         if (!isAttachmentComplete(m_depthAttachment.get(), GraphicsContext3D::DEPTH_ATTACHMENT))
320             return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
321         width = getImageWidth(m_depthAttachment.get());
322         height = getImageHeight(m_depthAttachment.get());
323         count++;
324     }
325     if (isStencilAttached()) {
326         if (!isAttachmentComplete(m_stencilAttachment.get(), GraphicsContext3D::STENCIL_ATTACHMENT))
327             return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
328         if (!count) {
329             width = getImageWidth(m_stencilAttachment.get());
330             height = getImageHeight(m_stencilAttachment.get());
331         } else {
332             if (width != getImageWidth(m_stencilAttachment.get()) || height != getImageHeight(m_stencilAttachment.get()))
333                 return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
334         }
335         count++;
336     }
337     if (isDepthStencilAttached()) {
338         if (!isAttachmentComplete(m_depthStencilAttachment.get(), GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT))
339             return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
340         if (!isValid(m_depthStencilAttachment.get()))
341             return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED;
342         if (!count) {
343             width = getImageWidth(m_depthStencilAttachment.get());
344             height = getImageHeight(m_depthStencilAttachment.get());
345         } else {
346             if (width != getImageWidth(m_depthStencilAttachment.get()) || height != getImageHeight(m_depthStencilAttachment.get()))
347                 return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
348         }
349         count++;
350     }
351     // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
352     if (count > 1)
353         return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED;
354     if (isColorAttached()) {
355         // FIXME: if color buffer is texture, is ALPHA, LUMINANCE or LUMINANCE_ALPHA valid?
356         if (!getColorBufferFormat())
357             return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
358         if (!count) {
359             if (!getColorBufferWidth() || !getColorBufferHeight())
360                 return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
361         } else {
362             if (width != getColorBufferWidth() || height != getColorBufferHeight())
363                 return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
364         }
365     } else {
366         if (!count)
367             return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
368     }
369     return GraphicsContext3D::FRAMEBUFFER_COMPLETE;
370 }
371
372 bool WebGLFramebuffer::onAccess(bool needToInitializeRenderbuffers)
373 {
374     if (checkStatus() != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
375         return false;
376     if (needToInitializeRenderbuffers)
377         return initializeRenderbuffers();
378     return true;
379 }
380
381 void WebGLFramebuffer::deleteObjectImpl(Platform3DObject object)
382 {
383     if (m_colorAttachment)
384         m_colorAttachment->onDetached();
385     if (m_depthAttachment)
386         m_depthAttachment->onDetached();
387     if (m_stencilAttachment)
388         m_stencilAttachment->onDetached();
389     if (m_depthStencilAttachment)
390         m_depthStencilAttachment->onDetached();
391     context()->graphicsContext3D()->deleteFramebuffer(object);
392 }
393
394 bool WebGLFramebuffer::initializeRenderbuffers()
395 {
396     ASSERT(object());
397     bool initColor = false, initDepth = false, initStencil = false;
398     GC3Dbitfield mask = 0;
399     if (isUninitialized(m_colorAttachment.get())) {
400         initColor = true;
401         mask |= GraphicsContext3D::COLOR_BUFFER_BIT;
402     }
403     if (isUninitialized(m_depthAttachment.get())) {
404         initDepth = true;
405         mask |= GraphicsContext3D::DEPTH_BUFFER_BIT;
406     }
407     if (isUninitialized(m_stencilAttachment.get())) {
408         initStencil = true;
409         mask |= GraphicsContext3D::STENCIL_BUFFER_BIT;
410     }
411     if (isUninitialized(m_depthStencilAttachment.get())) {
412         initDepth = true;
413         initStencil = true;
414         mask |= (GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT);
415     }
416     if (!initColor && !initDepth && !initStencil)
417         return true;
418
419     // We only clear un-initialized renderbuffers when they are ready to be
420     // read, i.e., when the framebuffer is complete.
421     GraphicsContext3D* g3d = context()->graphicsContext3D();
422     if (g3d->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
423         return false;
424
425     GC3Dfloat colorClearValue[] = {0, 0, 0, 0}, depthClearValue = 0;
426     GC3Dint stencilClearValue = 0;
427     GC3Dboolean colorMask[] = {0, 0, 0, 0}, depthMask = 0;
428     GC3Duint stencilMask = 0xffffffff;
429     GC3Dboolean isScissorEnabled = 0;
430     GC3Dboolean isDitherEnabled = 0;
431     if (initColor) {
432         g3d->getFloatv(GraphicsContext3D::COLOR_CLEAR_VALUE, colorClearValue);
433         g3d->getBooleanv(GraphicsContext3D::COLOR_WRITEMASK, colorMask);
434         g3d->clearColor(0, 0, 0, 0);
435         g3d->colorMask(true, true, true, true);
436     }
437     if (initDepth) {
438         g3d->getFloatv(GraphicsContext3D::DEPTH_CLEAR_VALUE, &depthClearValue);
439         g3d->getBooleanv(GraphicsContext3D::DEPTH_WRITEMASK, &depthMask);
440         g3d->clearDepth(0);
441         g3d->depthMask(true);
442     }
443     if (initStencil) {
444         g3d->getIntegerv(GraphicsContext3D::STENCIL_CLEAR_VALUE, &stencilClearValue);
445         g3d->getIntegerv(GraphicsContext3D::STENCIL_WRITEMASK, reinterpret_cast<GC3Dint*>(&stencilMask));
446         g3d->clearStencil(0);
447         g3d->stencilMask(0xffffffff);
448     }
449     isScissorEnabled = g3d->isEnabled(GraphicsContext3D::SCISSOR_TEST);
450     g3d->disable(GraphicsContext3D::SCISSOR_TEST);
451     isDitherEnabled = g3d->isEnabled(GraphicsContext3D::DITHER);
452     g3d->disable(GraphicsContext3D::DITHER);
453
454     g3d->clear(mask);
455
456     if (initColor) {
457         g3d->clearColor(colorClearValue[0], colorClearValue[1], colorClearValue[2], colorClearValue[3]);
458         g3d->colorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
459     }
460     if (initDepth) {
461         g3d->clearDepth(depthClearValue);
462         g3d->depthMask(depthMask);
463     }
464     if (initStencil) {
465         g3d->clearStencil(stencilClearValue);
466         g3d->stencilMask(stencilMask);
467     }
468     if (isScissorEnabled)
469         g3d->enable(GraphicsContext3D::SCISSOR_TEST);
470     else
471         g3d->disable(GraphicsContext3D::SCISSOR_TEST);
472     if (isDitherEnabled)
473         g3d->enable(GraphicsContext3D::DITHER);
474     else
475         g3d->disable(GraphicsContext3D::DITHER);
476
477     if (initColor)
478         setInitialized(m_colorAttachment.get());
479     if (initDepth && initStencil && m_depthStencilAttachment)
480         setInitialized(m_depthStencilAttachment.get());
481     else {
482         if (initDepth)
483             setInitialized(m_depthAttachment.get());
484         if (initStencil)
485             setInitialized(m_stencilAttachment.get());
486     }
487     return true;
488 }
489
490 bool WebGLFramebuffer::isBound() const
491 {
492     return (context()->m_framebufferBinding.get() == this);
493 }
494
495 }
496
497 #endif // ENABLE(WEBGL)