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