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 bool initialized() const OVERRIDE;
56 virtual void setInitialized() OVERRIDE;
57 virtual void onDetached(blink::WebGraphicsContext3D*) OVERRIDE;
58 virtual void attach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
59 virtual void unattach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
61 WebGLRenderbufferAttachment() { };
63 RefPtr<WebGLRenderbuffer> m_renderbuffer;
66 PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLRenderbufferAttachment::create(WebGLRenderbuffer* renderbuffer)
68 return adoptRef(new WebGLRenderbufferAttachment(renderbuffer));
71 WebGLRenderbufferAttachment::WebGLRenderbufferAttachment(WebGLRenderbuffer* renderbuffer)
72 : m_renderbuffer(renderbuffer)
76 GLsizei WebGLRenderbufferAttachment::width() const
78 return m_renderbuffer->width();
81 GLsizei WebGLRenderbufferAttachment::height() const
83 return m_renderbuffer->height();
86 GLenum WebGLRenderbufferAttachment::format() const
88 GLenum format = m_renderbuffer->internalFormat();
89 if (format == GL_DEPTH_STENCIL_OES
90 && m_renderbuffer->emulatedStencilBuffer()
91 && m_renderbuffer->emulatedStencilBuffer()->internalFormat() != GL_STENCIL_INDEX8) {
97 WebGLSharedObject* WebGLRenderbufferAttachment::object() const
99 return m_renderbuffer->object() ? m_renderbuffer.get() : 0;
102 bool WebGLRenderbufferAttachment::isSharedObject(WebGLSharedObject* object) const
104 return object == m_renderbuffer;
107 bool WebGLRenderbufferAttachment::valid() const
109 return m_renderbuffer->object();
112 bool WebGLRenderbufferAttachment::initialized() const
114 return m_renderbuffer->object() && m_renderbuffer->initialized();
117 void WebGLRenderbufferAttachment::setInitialized()
119 if (m_renderbuffer->object())
120 m_renderbuffer->setInitialized();
123 void WebGLRenderbufferAttachment::onDetached(blink::WebGraphicsContext3D* context)
125 m_renderbuffer->onDetached(context);
128 void WebGLRenderbufferAttachment::attach(blink::WebGraphicsContext3D* context, GLenum attachment)
130 Platform3DObject object = objectOrZero(m_renderbuffer.get());
131 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL && m_renderbuffer->emulatedStencilBuffer()) {
132 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, object);
133 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, objectOrZero(m_renderbuffer->emulatedStencilBuffer()));
135 context->framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, object);
139 void WebGLRenderbufferAttachment::unattach(blink::WebGraphicsContext3D* context, GLenum attachment)
141 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
142 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
143 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
145 context->framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, 0);
149 GLenum WebGLRenderbufferAttachment::type() const
155 class WebGLTextureAttachment FINAL : public WebGLFramebuffer::WebGLAttachment {
157 static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLTexture*, GLenum target, GLint level);
160 WebGLTextureAttachment(WebGLTexture*, GLenum target, GLint level);
161 virtual GLsizei width() const OVERRIDE;
162 virtual GLsizei height() const OVERRIDE;
163 virtual GLenum format() const OVERRIDE;
164 virtual GLenum type() const OVERRIDE;
165 virtual WebGLSharedObject* object() const OVERRIDE;
166 virtual bool isSharedObject(WebGLSharedObject*) const OVERRIDE;
167 virtual bool valid() const OVERRIDE;
168 virtual bool initialized() const OVERRIDE;
169 virtual void setInitialized() OVERRIDE;
170 virtual void onDetached(blink::WebGraphicsContext3D*) OVERRIDE;
171 virtual void attach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
172 virtual void unattach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
174 WebGLTextureAttachment() { };
176 RefPtr<WebGLTexture> m_texture;
181 PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLTextureAttachment::create(WebGLTexture* texture, GLenum target, GLint level)
183 return adoptRef(new WebGLTextureAttachment(texture, target, level));
186 WebGLTextureAttachment::WebGLTextureAttachment(WebGLTexture* texture, GLenum target, GLint level)
193 GLsizei WebGLTextureAttachment::width() const
195 return m_texture->getWidth(m_target, m_level);
198 GLsizei WebGLTextureAttachment::height() const
200 return m_texture->getHeight(m_target, m_level);
203 GLenum WebGLTextureAttachment::format() const
205 return m_texture->getInternalFormat(m_target, m_level);
208 WebGLSharedObject* WebGLTextureAttachment::object() const
210 return m_texture->object() ? m_texture.get() : 0;
213 bool WebGLTextureAttachment::isSharedObject(WebGLSharedObject* object) const
215 return object == m_texture;
218 bool WebGLTextureAttachment::valid() const
220 return m_texture->object();
223 bool WebGLTextureAttachment::initialized() const
225 // Textures are assumed to be initialized.
229 void WebGLTextureAttachment::setInitialized()
231 // Textures are assumed to be initialized.
234 void WebGLTextureAttachment::onDetached(blink::WebGraphicsContext3D* context)
236 m_texture->onDetached(context);
239 void WebGLTextureAttachment::attach(blink::WebGraphicsContext3D* context, GLenum attachment)
241 Platform3DObject object = objectOrZero(m_texture.get());
242 context->framebufferTexture2D(GL_FRAMEBUFFER, attachment, m_target, object, m_level);
245 void WebGLTextureAttachment::unattach(blink::WebGraphicsContext3D* context, GLenum attachment)
247 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
248 context->framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_target, 0, m_level);
249 context->framebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, m_target, 0, m_level);
251 context->framebufferTexture2D(GL_FRAMEBUFFER, attachment, m_target, 0, m_level);
255 GLenum WebGLTextureAttachment::type() const
257 return m_texture->getType(m_target, m_level);
260 bool isColorRenderable(GLenum internalformat)
262 switch (internalformat) {
272 } // anonymous namespace
274 WebGLFramebuffer::WebGLAttachment::WebGLAttachment()
278 WebGLFramebuffer::WebGLAttachment::~WebGLAttachment()
282 PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx)
284 return adoptRef(new WebGLFramebuffer(ctx));
287 WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx)
288 : WebGLContextObject(ctx)
289 , m_hasEverBeenBound(false)
291 ScriptWrappable::init(this);
292 setObject(ctx->webGraphicsContext3D()->createFramebuffer());
295 WebGLFramebuffer::~WebGLFramebuffer()
300 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GLenum attachment, GLenum texTarget, WebGLTexture* texture, GLint level)
303 removeAttachmentFromBoundFramebuffer(attachment);
306 if (texture && texture->object()) {
307 m_attachments.add(attachment, WebGLTextureAttachment::create(texture, texTarget, level));
308 drawBuffersIfNecessary(false);
309 texture->onAttached();
313 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GLenum attachment, WebGLRenderbuffer* renderbuffer)
316 removeAttachmentFromBoundFramebuffer(attachment);
319 if (renderbuffer && renderbuffer->object()) {
320 m_attachments.add(attachment, WebGLRenderbufferAttachment::create(renderbuffer));
321 drawBuffersIfNecessary(false);
322 renderbuffer->onAttached();
326 void WebGLFramebuffer::attach(GLenum attachment, GLenum attachmentPoint)
329 WebGLAttachment* attachmentObject = getAttachment(attachment);
330 if (attachmentObject)
331 attachmentObject->attach(context()->webGraphicsContext3D(), attachmentPoint);
334 WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GLenum attachment) const
338 WebGLAttachment* attachmentObject = getAttachment(attachment);
339 return attachmentObject ? attachmentObject->object() : 0;
342 bool WebGLFramebuffer::isAttachmentComplete(WebGLAttachment* attachedObject, GLenum attachment, const char** reason) const
344 ASSERT(attachedObject && attachedObject->valid());
347 GLenum internalformat = attachedObject->format();
348 WebGLSharedObject* object = attachedObject->object();
349 ASSERT(object && (object->isTexture() || object->isRenderbuffer()));
351 if (attachment == GL_DEPTH_ATTACHMENT) {
352 if (object->isRenderbuffer()) {
353 if (internalformat != GL_DEPTH_COMPONENT16) {
354 *reason = "the internalformat of the attached renderbuffer is not DEPTH_COMPONENT16";
357 } else if (object->isTexture()) {
358 GLenum type = attachedObject->type();
359 if (!(context()->m_webglDepthTexture && internalformat == GL_DEPTH_COMPONENT
360 && (type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT))) {
361 *reason = "the attached texture is not a depth texture";
365 } else if (attachment == GL_STENCIL_ATTACHMENT) {
366 // Depend on the underlying GL drivers to check stencil textures
367 // and check renderbuffer type here only.
368 if (object->isRenderbuffer()) {
369 if (internalformat != GL_STENCIL_INDEX8) {
370 *reason = "the internalformat of the attached renderbuffer is not STENCIL_INDEX8";
374 } else if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
375 if (object->isRenderbuffer()) {
376 if (internalformat != GL_DEPTH_STENCIL_OES) {
377 *reason = "the internalformat of the attached renderbuffer is not DEPTH_STENCIL";
380 } else if (object->isTexture()) {
381 GLenum type = attachedObject->type();
382 if (!(context()->m_webglDepthTexture && internalformat == GL_DEPTH_STENCIL_OES
383 && type == GL_UNSIGNED_INT_24_8_OES)) {
384 *reason = "the attached texture is not a DEPTH_STENCIL texture";
388 } else if (attachment == GL_COLOR_ATTACHMENT0
389 || (context()->m_webglDrawBuffers && attachment > GL_COLOR_ATTACHMENT0
390 && attachment < static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + context()->maxColorAttachments()))) {
391 if (object->isRenderbuffer()) {
392 if (!isColorRenderable(internalformat)) {
393 *reason = "the internalformat of the attached renderbuffer is not color-renderable";
396 } else if (object->isTexture()) {
397 GLenum type = attachedObject->type();
398 if (internalformat != GL_RGBA && internalformat != GL_RGB) {
399 *reason = "the internalformat of the attached texture is not color-renderable";
402 // TODO: WEBGL_color_buffer_float and EXT_color_buffer_half_float extensions have not been implemented in
403 // WebGL yet. It would be better to depend on the underlying GL drivers to check on rendering to floating point textures
404 // and add the check back to WebGL when above two extensions are implemented.
405 // Assume UNSIGNED_BYTE is renderable here without the need to explicitly check if GL_OES_rgb8_rgba8 extension is supported.
406 if (type != GL_UNSIGNED_BYTE
407 && type != GL_UNSIGNED_SHORT_5_6_5
408 && type != GL_UNSIGNED_SHORT_4_4_4_4
409 && type != GL_UNSIGNED_SHORT_5_5_5_1
410 && !(type == GL_FLOAT && context()->m_oesTextureFloat)
411 && !(type == GL_HALF_FLOAT_OES && context()->m_oesTextureHalfFloat)) {
412 *reason = "unsupported type: The attached texture is not supported to be rendered to";
417 *reason = "unknown framebuffer attachment point";
421 if (!attachedObject->width() || !attachedObject->height()) {
422 *reason = "attachment has a 0 dimension";
428 WebGLFramebuffer::WebGLAttachment* WebGLFramebuffer::getAttachment(GLenum attachment) const
430 const AttachmentMap::const_iterator it = m_attachments.find(attachment);
431 return (it != m_attachments.end()) ? it->value.get() : 0;
434 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GLenum attachment)
440 WebGLAttachment* attachmentObject = getAttachment(attachment);
441 if (attachmentObject) {
442 attachmentObject->onDetached(context()->webGraphicsContext3D());
443 m_attachments.remove(attachment);
444 drawBuffersIfNecessary(false);
445 switch (attachment) {
446 case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL:
447 attach(GL_DEPTH_ATTACHMENT, GL_DEPTH_ATTACHMENT);
448 attach(GL_STENCIL_ATTACHMENT, GL_STENCIL_ATTACHMENT);
450 case GL_DEPTH_ATTACHMENT:
451 attach(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL, GL_DEPTH_ATTACHMENT);
453 case GL_STENCIL_ATTACHMENT:
454 attach(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL, GL_STENCIL_ATTACHMENT);
460 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLSharedObject* attachment)
468 bool checkMore = true;
471 for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
472 WebGLAttachment* attachmentObject = it->value.get();
473 if (attachmentObject->isSharedObject(attachment)) {
474 GLenum attachmentType = it->key;
475 attachmentObject->unattach(context()->webGraphicsContext3D(), attachmentType);
476 removeAttachmentFromBoundFramebuffer(attachmentType);
484 GLsizei WebGLFramebuffer::colorBufferWidth() const
488 WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0);
492 return attachment->width();
495 GLsizei WebGLFramebuffer::colorBufferHeight() const
499 WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0);
503 return attachment->height();
506 GLenum WebGLFramebuffer::colorBufferFormat() const
510 WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0);
513 return attachment->format();
516 GLenum WebGLFramebuffer::checkStatus(const char** reason) const
518 unsigned int count = 0;
519 GLsizei width = 0, height = 0;
520 bool haveDepth = false;
521 bool haveStencil = false;
522 bool haveDepthStencil = false;
523 for (AttachmentMap::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
524 WebGLAttachment* attachment = it->value.get();
525 if (!isAttachmentComplete(attachment, it->key, reason))
526 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
527 if (!attachment->valid()) {
528 *reason = "attachment is not valid";
529 return GL_FRAMEBUFFER_UNSUPPORTED;
531 if (!attachment->format()) {
532 *reason = "attachment is an unsupported format";
533 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
536 case GL_DEPTH_ATTACHMENT:
539 case GL_STENCIL_ATTACHMENT:
542 case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL:
543 haveDepthStencil = true;
547 width = attachment->width();
548 height = attachment->height();
550 if (width != attachment->width() || height != attachment->height()) {
551 *reason = "attachments do not have the same dimensions";
552 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
558 *reason = "no attachments";
559 return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
561 if (!width || !height) {
562 *reason = "framebuffer has a 0 dimension";
563 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
565 // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
566 if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) {
567 *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments";
568 return GL_FRAMEBUFFER_UNSUPPORTED;
570 return GL_FRAMEBUFFER_COMPLETE;
573 bool WebGLFramebuffer::onAccess(blink::WebGraphicsContext3D* context3d, const char** reason)
575 if (checkStatus(reason) != GL_FRAMEBUFFER_COMPLETE)
580 bool WebGLFramebuffer::hasStencilBuffer() const
582 WebGLAttachment* attachment = getAttachment(GL_STENCIL_ATTACHMENT);
584 attachment = getAttachment(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL);
585 return attachment && attachment->valid();
588 void WebGLFramebuffer::deleteObjectImpl(blink::WebGraphicsContext3D* context3d, Platform3DObject object)
590 for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
591 it->value->onDetached(context3d);
593 context3d->deleteFramebuffer(object);
596 bool WebGLFramebuffer::isBound() const
598 return (context()->m_framebufferBinding.get() == this);
601 void WebGLFramebuffer::drawBuffers(const Vector<GLenum>& bufs)
603 m_drawBuffers = bufs;
604 m_filteredDrawBuffers.resize(m_drawBuffers.size());
605 for (size_t i = 0; i < m_filteredDrawBuffers.size(); ++i)
606 m_filteredDrawBuffers[i] = GL_NONE;
607 drawBuffersIfNecessary(true);
610 void WebGLFramebuffer::drawBuffersIfNecessary(bool force)
612 if (!context()->m_webglDrawBuffers)
615 // This filtering works around graphics driver bugs on Mac OS X.
616 for (size_t i = 0; i < m_drawBuffers.size(); ++i) {
617 if (m_drawBuffers[i] != GL_NONE && getAttachment(m_drawBuffers[i])) {
618 if (m_filteredDrawBuffers[i] != m_drawBuffers[i]) {
619 m_filteredDrawBuffers[i] = m_drawBuffers[i];
623 if (m_filteredDrawBuffers[i] != GL_NONE) {
624 m_filteredDrawBuffers[i] = GL_NONE;
630 context()->webGraphicsContext3D()->drawBuffersEXT(
631 m_filteredDrawBuffers.size(), m_filteredDrawBuffers.data());
635 GLenum WebGLFramebuffer::getDrawBuffer(GLenum drawBuffer)
637 int index = static_cast<int>(drawBuffer - GL_DRAW_BUFFER0_EXT);
639 if (index < static_cast<int>(m_drawBuffers.size()))
640 return m_drawBuffers[index];
641 if (drawBuffer == GL_DRAW_BUFFER0_EXT)
642 return GL_COLOR_ATTACHMENT0;