2 * Copyright (C) 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #include "core/html/canvas/WebGLFramebuffer.h"
30 #include "core/html/canvas/WebGLRenderingContext.h"
31 #include "platform/NotImplemented.h"
37 Platform3DObject objectOrZero(WebGLObject* object)
39 return object ? object->object() : 0;
42 class WebGLRenderbufferAttachment FINAL : public WebGLFramebuffer::WebGLAttachment {
44 static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLRenderbuffer*);
47 WebGLRenderbufferAttachment(WebGLRenderbuffer*);
48 virtual GLsizei width() const OVERRIDE;
49 virtual GLsizei height() const OVERRIDE;
50 virtual GLenum format() const OVERRIDE;
51 virtual GLenum type() const OVERRIDE;
52 virtual WebGLSharedObject* object() const OVERRIDE;
53 virtual bool isSharedObject(WebGLSharedObject*) const OVERRIDE;
54 virtual bool valid() const OVERRIDE;
55 virtual void onDetached(blink::WebGraphicsContext3D*) OVERRIDE;
56 virtual void attach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
57 virtual void unattach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
59 WebGLRenderbufferAttachment() { };
61 RefPtr<WebGLRenderbuffer> m_renderbuffer;
64 PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLRenderbufferAttachment::create(WebGLRenderbuffer* renderbuffer)
66 return adoptRef(new WebGLRenderbufferAttachment(renderbuffer));
69 WebGLRenderbufferAttachment::WebGLRenderbufferAttachment(WebGLRenderbuffer* renderbuffer)
70 : m_renderbuffer(renderbuffer)
74 GLsizei WebGLRenderbufferAttachment::width() const
76 return m_renderbuffer->width();
79 GLsizei WebGLRenderbufferAttachment::height() const
81 return m_renderbuffer->height();
84 GLenum WebGLRenderbufferAttachment::format() const
86 GLenum format = m_renderbuffer->internalFormat();
87 if (format == GL_DEPTH_STENCIL_OES
88 && m_renderbuffer->emulatedStencilBuffer()
89 && m_renderbuffer->emulatedStencilBuffer()->internalFormat() != GL_STENCIL_INDEX8) {
95 WebGLSharedObject* WebGLRenderbufferAttachment::object() const
97 return m_renderbuffer->object() ? m_renderbuffer.get() : 0;
100 bool WebGLRenderbufferAttachment::isSharedObject(WebGLSharedObject* object) const
102 return object == m_renderbuffer;
105 bool WebGLRenderbufferAttachment::valid() const
107 return m_renderbuffer->object();
110 void WebGLRenderbufferAttachment::onDetached(blink::WebGraphicsContext3D* context)
112 m_renderbuffer->onDetached(context);
115 void WebGLRenderbufferAttachment::attach(blink::WebGraphicsContext3D* context, GLenum attachment)
117 Platform3DObject object = objectOrZero(m_renderbuffer.get());
118 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL && m_renderbuffer->emulatedStencilBuffer()) {
119 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, object);
120 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, objectOrZero(m_renderbuffer->emulatedStencilBuffer()));
122 context->framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, object);
126 void WebGLRenderbufferAttachment::unattach(blink::WebGraphicsContext3D* context, GLenum attachment)
128 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
129 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
130 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
132 context->framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, 0);
136 GLenum WebGLRenderbufferAttachment::type() const
142 class WebGLTextureAttachment FINAL : public WebGLFramebuffer::WebGLAttachment {
144 static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLTexture*, GLenum target, GLint level);
147 WebGLTextureAttachment(WebGLTexture*, GLenum target, GLint level);
148 virtual GLsizei width() const OVERRIDE;
149 virtual GLsizei height() const OVERRIDE;
150 virtual GLenum format() const OVERRIDE;
151 virtual GLenum type() const OVERRIDE;
152 virtual WebGLSharedObject* object() const OVERRIDE;
153 virtual bool isSharedObject(WebGLSharedObject*) const OVERRIDE;
154 virtual bool valid() const OVERRIDE;
155 virtual void onDetached(blink::WebGraphicsContext3D*) OVERRIDE;
156 virtual void attach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
157 virtual void unattach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
159 WebGLTextureAttachment() { };
161 RefPtr<WebGLTexture> m_texture;
166 PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLTextureAttachment::create(WebGLTexture* texture, GLenum target, GLint level)
168 return adoptRef(new WebGLTextureAttachment(texture, target, level));
171 WebGLTextureAttachment::WebGLTextureAttachment(WebGLTexture* texture, GLenum target, GLint level)
178 GLsizei WebGLTextureAttachment::width() const
180 return m_texture->getWidth(m_target, m_level);
183 GLsizei WebGLTextureAttachment::height() const
185 return m_texture->getHeight(m_target, m_level);
188 GLenum WebGLTextureAttachment::format() const
190 return m_texture->getInternalFormat(m_target, m_level);
193 WebGLSharedObject* WebGLTextureAttachment::object() const
195 return m_texture->object() ? m_texture.get() : 0;
198 bool WebGLTextureAttachment::isSharedObject(WebGLSharedObject* object) const
200 return object == m_texture;
203 bool WebGLTextureAttachment::valid() const
205 return m_texture->object();
208 void WebGLTextureAttachment::onDetached(blink::WebGraphicsContext3D* context)
210 m_texture->onDetached(context);
213 void WebGLTextureAttachment::attach(blink::WebGraphicsContext3D* context, GLenum attachment)
215 Platform3DObject object = objectOrZero(m_texture.get());
216 context->framebufferTexture2D(GL_FRAMEBUFFER, attachment, m_target, object, m_level);
219 void WebGLTextureAttachment::unattach(blink::WebGraphicsContext3D* context, GLenum attachment)
221 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
222 context->framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_target, 0, m_level);
223 context->framebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, m_target, 0, m_level);
225 context->framebufferTexture2D(GL_FRAMEBUFFER, attachment, m_target, 0, m_level);
229 GLenum WebGLTextureAttachment::type() const
231 return m_texture->getType(m_target, m_level);
234 bool isColorRenderable(GLenum internalformat)
236 switch (internalformat) {
246 } // anonymous namespace
248 WebGLFramebuffer::WebGLAttachment::WebGLAttachment()
252 WebGLFramebuffer::WebGLAttachment::~WebGLAttachment()
256 PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx)
258 return adoptRef(new WebGLFramebuffer(ctx));
261 WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx)
262 : WebGLContextObject(ctx)
263 , m_hasEverBeenBound(false)
265 ScriptWrappable::init(this);
266 setObject(ctx->webGraphicsContext3D()->createFramebuffer());
269 WebGLFramebuffer::~WebGLFramebuffer()
274 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GLenum attachment, GLenum texTarget, WebGLTexture* texture, GLint level)
277 removeAttachmentFromBoundFramebuffer(attachment);
280 if (texture && texture->object()) {
281 m_attachments.add(attachment, WebGLTextureAttachment::create(texture, texTarget, level));
282 drawBuffersIfNecessary(false);
283 texture->onAttached();
287 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GLenum attachment, WebGLRenderbuffer* renderbuffer)
290 removeAttachmentFromBoundFramebuffer(attachment);
293 if (renderbuffer && renderbuffer->object()) {
294 m_attachments.add(attachment, WebGLRenderbufferAttachment::create(renderbuffer));
295 drawBuffersIfNecessary(false);
296 renderbuffer->onAttached();
300 void WebGLFramebuffer::attach(GLenum attachment, GLenum attachmentPoint)
303 WebGLAttachment* attachmentObject = getAttachment(attachment);
304 if (attachmentObject)
305 attachmentObject->attach(context()->webGraphicsContext3D(), attachmentPoint);
308 WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GLenum attachment) const
312 WebGLAttachment* attachmentObject = getAttachment(attachment);
313 return attachmentObject ? attachmentObject->object() : 0;
316 bool WebGLFramebuffer::isAttachmentComplete(WebGLAttachment* attachedObject, GLenum attachment, const char** reason) const
318 ASSERT(attachedObject && attachedObject->valid());
321 GLenum internalformat = attachedObject->format();
322 WebGLSharedObject* object = attachedObject->object();
323 ASSERT(object && (object->isTexture() || object->isRenderbuffer()));
325 if (attachment == GL_DEPTH_ATTACHMENT) {
326 if (object->isRenderbuffer()) {
327 if (internalformat != GL_DEPTH_COMPONENT16) {
328 *reason = "the internalformat of the attached renderbuffer is not DEPTH_COMPONENT16";
331 } else if (object->isTexture()) {
332 GLenum type = attachedObject->type();
333 if (!(context()->m_webglDepthTexture && internalformat == GL_DEPTH_COMPONENT
334 && (type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT))) {
335 *reason = "the attached texture is not a depth texture";
339 } else if (attachment == GL_STENCIL_ATTACHMENT) {
340 // Depend on the underlying GL drivers to check stencil textures
341 // and check renderbuffer type here only.
342 if (object->isRenderbuffer()) {
343 if (internalformat != GL_STENCIL_INDEX8) {
344 *reason = "the internalformat of the attached renderbuffer is not STENCIL_INDEX8";
348 } else if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
349 if (object->isRenderbuffer()) {
350 if (internalformat != GL_DEPTH_STENCIL_OES) {
351 *reason = "the internalformat of the attached renderbuffer is not DEPTH_STENCIL";
354 } else if (object->isTexture()) {
355 GLenum type = attachedObject->type();
356 if (!(context()->m_webglDepthTexture && internalformat == GL_DEPTH_STENCIL_OES
357 && type == GL_UNSIGNED_INT_24_8_OES)) {
358 *reason = "the attached texture is not a DEPTH_STENCIL texture";
362 } else if (attachment == GL_COLOR_ATTACHMENT0
363 || (context()->m_webglDrawBuffers && attachment > GL_COLOR_ATTACHMENT0
364 && attachment < static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + context()->maxColorAttachments()))) {
365 if (object->isRenderbuffer()) {
366 if (!isColorRenderable(internalformat)) {
367 *reason = "the internalformat of the attached renderbuffer is not color-renderable";
370 } else if (object->isTexture()) {
371 GLenum type = attachedObject->type();
372 if (internalformat != GL_RGBA && internalformat != GL_RGB) {
373 *reason = "the internalformat of the attached texture is not color-renderable";
376 // TODO: WEBGL_color_buffer_float and EXT_color_buffer_half_float extensions have not been implemented in
377 // WebGL yet. It would be better to depend on the underlying GL drivers to check on rendering to floating point textures
378 // and add the check back to WebGL when above two extensions are implemented.
379 // Assume UNSIGNED_BYTE is renderable here without the need to explicitly check if GL_OES_rgb8_rgba8 extension is supported.
380 if (type != GL_UNSIGNED_BYTE
381 && type != GL_UNSIGNED_SHORT_5_6_5
382 && type != GL_UNSIGNED_SHORT_4_4_4_4
383 && type != GL_UNSIGNED_SHORT_5_5_5_1
384 && !(type == GL_FLOAT && context()->m_oesTextureFloat)
385 && !(type == GL_HALF_FLOAT_OES && context()->m_oesTextureHalfFloat)) {
386 *reason = "unsupported type: The attached texture is not supported to be rendered to";
391 *reason = "unknown framebuffer attachment point";
395 if (!attachedObject->width() || !attachedObject->height()) {
396 *reason = "attachment has a 0 dimension";
402 WebGLFramebuffer::WebGLAttachment* WebGLFramebuffer::getAttachment(GLenum attachment) const
404 const AttachmentMap::const_iterator it = m_attachments.find(attachment);
405 return (it != m_attachments.end()) ? it->value.get() : 0;
408 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GLenum attachment)
414 WebGLAttachment* attachmentObject = getAttachment(attachment);
415 if (attachmentObject) {
416 attachmentObject->onDetached(context()->webGraphicsContext3D());
417 m_attachments.remove(attachment);
418 drawBuffersIfNecessary(false);
419 switch (attachment) {
420 case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL:
421 attach(GL_DEPTH_ATTACHMENT, GL_DEPTH_ATTACHMENT);
422 attach(GL_STENCIL_ATTACHMENT, GL_STENCIL_ATTACHMENT);
424 case GL_DEPTH_ATTACHMENT:
425 attach(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL, GL_DEPTH_ATTACHMENT);
427 case GL_STENCIL_ATTACHMENT:
428 attach(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL, GL_STENCIL_ATTACHMENT);
434 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLSharedObject* attachment)
442 bool checkMore = true;
445 for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
446 WebGLAttachment* attachmentObject = it->value.get();
447 if (attachmentObject->isSharedObject(attachment)) {
448 GLenum attachmentType = it->key;
449 attachmentObject->unattach(context()->webGraphicsContext3D(), attachmentType);
450 removeAttachmentFromBoundFramebuffer(attachmentType);
458 GLsizei WebGLFramebuffer::colorBufferWidth() const
462 WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0);
466 return attachment->width();
469 GLsizei WebGLFramebuffer::colorBufferHeight() const
473 WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0);
477 return attachment->height();
480 GLenum WebGLFramebuffer::colorBufferFormat() const
484 WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0);
487 return attachment->format();
490 GLenum WebGLFramebuffer::checkStatus(const char** reason) const
492 unsigned int count = 0;
493 GLsizei width = 0, height = 0;
494 bool haveDepth = false;
495 bool haveStencil = false;
496 bool haveDepthStencil = false;
497 for (AttachmentMap::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
498 WebGLAttachment* attachment = it->value.get();
499 if (!isAttachmentComplete(attachment, it->key, reason))
500 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
501 if (!attachment->valid()) {
502 *reason = "attachment is not valid";
503 return GL_FRAMEBUFFER_UNSUPPORTED;
505 if (!attachment->format()) {
506 *reason = "attachment is an unsupported format";
507 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
510 case GL_DEPTH_ATTACHMENT:
513 case GL_STENCIL_ATTACHMENT:
516 case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL:
517 haveDepthStencil = true;
521 width = attachment->width();
522 height = attachment->height();
524 if (width != attachment->width() || height != attachment->height()) {
525 *reason = "attachments do not have the same dimensions";
526 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
532 *reason = "no attachments";
533 return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
535 if (!width || !height) {
536 *reason = "framebuffer has a 0 dimension";
537 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
539 // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
540 if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) {
541 *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments";
542 return GL_FRAMEBUFFER_UNSUPPORTED;
544 return GL_FRAMEBUFFER_COMPLETE;
547 bool WebGLFramebuffer::onAccess(blink::WebGraphicsContext3D* context3d, const char** reason)
549 if (checkStatus(reason) != GL_FRAMEBUFFER_COMPLETE)
554 bool WebGLFramebuffer::hasStencilBuffer() const
556 WebGLAttachment* attachment = getAttachment(GL_STENCIL_ATTACHMENT);
558 attachment = getAttachment(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL);
559 return attachment && attachment->valid();
562 void WebGLFramebuffer::deleteObjectImpl(blink::WebGraphicsContext3D* context3d, Platform3DObject object)
564 for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
565 it->value->onDetached(context3d);
567 context3d->deleteFramebuffer(object);
570 bool WebGLFramebuffer::isBound() const
572 return (context()->m_framebufferBinding.get() == this);
575 void WebGLFramebuffer::drawBuffers(const Vector<GLenum>& bufs)
577 m_drawBuffers = bufs;
578 m_filteredDrawBuffers.resize(m_drawBuffers.size());
579 for (size_t i = 0; i < m_filteredDrawBuffers.size(); ++i)
580 m_filteredDrawBuffers[i] = GL_NONE;
581 drawBuffersIfNecessary(true);
584 void WebGLFramebuffer::drawBuffersIfNecessary(bool force)
586 if (!context()->m_webglDrawBuffers)
589 // This filtering works around graphics driver bugs on Mac OS X.
590 for (size_t i = 0; i < m_drawBuffers.size(); ++i) {
591 if (m_drawBuffers[i] != GL_NONE && getAttachment(m_drawBuffers[i])) {
592 if (m_filteredDrawBuffers[i] != m_drawBuffers[i]) {
593 m_filteredDrawBuffers[i] = m_drawBuffers[i];
597 if (m_filteredDrawBuffers[i] != GL_NONE) {
598 m_filteredDrawBuffers[i] = GL_NONE;
604 context()->webGraphicsContext3D()->drawBuffersEXT(
605 m_filteredDrawBuffers.size(), m_filteredDrawBuffers.data());
609 GLenum WebGLFramebuffer::getDrawBuffer(GLenum drawBuffer)
611 int index = static_cast<int>(drawBuffer - GL_DRAW_BUFFER0_EXT);
613 if (index < static_cast<int>(m_drawBuffers.size()))
614 return m_drawBuffers[index];
615 if (drawBuffer == GL_DRAW_BUFFER0_EXT)
616 return GL_COLOR_ATTACHMENT0;