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/WebGLRenderingContextBase.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 PassRefPtrWillBeRawPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLRenderbuffer*);
46 virtual void trace(Visitor*) OVERRIDE;
49 explicit WebGLRenderbufferAttachment(WebGLRenderbuffer*);
50 WebGLRenderbufferAttachment() { }
52 virtual GLsizei width() const OVERRIDE;
53 virtual GLsizei height() const OVERRIDE;
54 virtual GLenum format() const OVERRIDE;
55 virtual GLenum type() const OVERRIDE;
56 virtual WebGLSharedObject* object() const OVERRIDE;
57 virtual bool isSharedObject(WebGLSharedObject*) const OVERRIDE;
58 virtual bool valid() const OVERRIDE;
59 virtual void onDetached(blink::WebGraphicsContext3D*) OVERRIDE;
60 virtual void attach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
61 virtual void unattach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
63 RefPtrWillBeMember<WebGLRenderbuffer> m_renderbuffer;
66 PassRefPtrWillBeRawPtr<WebGLFramebuffer::WebGLAttachment> WebGLRenderbufferAttachment::create(WebGLRenderbuffer* renderbuffer)
68 return adoptRefWillBeNoop(new WebGLRenderbufferAttachment(renderbuffer));
71 void WebGLRenderbufferAttachment::trace(Visitor* visitor)
73 visitor->trace(m_renderbuffer);
74 WebGLFramebuffer::WebGLAttachment::trace(visitor);
77 WebGLRenderbufferAttachment::WebGLRenderbufferAttachment(WebGLRenderbuffer* renderbuffer)
78 : m_renderbuffer(renderbuffer)
82 GLsizei WebGLRenderbufferAttachment::width() const
84 return m_renderbuffer->width();
87 GLsizei WebGLRenderbufferAttachment::height() const
89 return m_renderbuffer->height();
92 GLenum WebGLRenderbufferAttachment::format() const
94 GLenum format = m_renderbuffer->internalFormat();
95 if (format == GL_DEPTH_STENCIL_OES
96 && m_renderbuffer->emulatedStencilBuffer()
97 && m_renderbuffer->emulatedStencilBuffer()->internalFormat() != GL_STENCIL_INDEX8) {
103 WebGLSharedObject* WebGLRenderbufferAttachment::object() const
105 return m_renderbuffer->object() ? m_renderbuffer.get() : 0;
108 bool WebGLRenderbufferAttachment::isSharedObject(WebGLSharedObject* object) const
110 return object == m_renderbuffer;
113 bool WebGLRenderbufferAttachment::valid() const
115 return m_renderbuffer->object();
118 void WebGLRenderbufferAttachment::onDetached(blink::WebGraphicsContext3D* context)
120 m_renderbuffer->onDetached(context);
123 void WebGLRenderbufferAttachment::attach(blink::WebGraphicsContext3D* context, GLenum attachment)
125 Platform3DObject object = objectOrZero(m_renderbuffer.get());
126 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL && m_renderbuffer->emulatedStencilBuffer()) {
127 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, object);
128 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, objectOrZero(m_renderbuffer->emulatedStencilBuffer()));
130 context->framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, object);
134 void WebGLRenderbufferAttachment::unattach(blink::WebGraphicsContext3D* context, GLenum attachment)
136 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
137 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
138 context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
140 context->framebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, 0);
144 GLenum WebGLRenderbufferAttachment::type() const
150 class WebGLTextureAttachment FINAL : public WebGLFramebuffer::WebGLAttachment {
152 static PassRefPtrWillBeRawPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLTexture*, GLenum target, GLint level);
154 virtual void trace(Visitor*) OVERRIDE;
157 WebGLTextureAttachment(WebGLTexture*, GLenum target, GLint level);
158 WebGLTextureAttachment() { }
160 virtual GLsizei width() const OVERRIDE;
161 virtual GLsizei height() const OVERRIDE;
162 virtual GLenum format() const OVERRIDE;
163 virtual GLenum type() const OVERRIDE;
164 virtual WebGLSharedObject* object() const OVERRIDE;
165 virtual bool isSharedObject(WebGLSharedObject*) const OVERRIDE;
166 virtual bool valid() const OVERRIDE;
167 virtual void onDetached(blink::WebGraphicsContext3D*) OVERRIDE;
168 virtual void attach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
169 virtual void unattach(blink::WebGraphicsContext3D*, GLenum attachment) OVERRIDE;
171 RefPtrWillBeMember<WebGLTexture> m_texture;
176 PassRefPtrWillBeRawPtr<WebGLFramebuffer::WebGLAttachment> WebGLTextureAttachment::create(WebGLTexture* texture, GLenum target, GLint level)
178 return adoptRefWillBeNoop(new WebGLTextureAttachment(texture, target, level));
181 void WebGLTextureAttachment::trace(Visitor* visitor)
183 visitor->trace(m_texture);
184 WebGLFramebuffer::WebGLAttachment::trace(visitor);
187 WebGLTextureAttachment::WebGLTextureAttachment(WebGLTexture* texture, GLenum target, GLint level)
194 GLsizei WebGLTextureAttachment::width() const
196 return m_texture->getWidth(m_target, m_level);
199 GLsizei WebGLTextureAttachment::height() const
201 return m_texture->getHeight(m_target, m_level);
204 GLenum WebGLTextureAttachment::format() const
206 return m_texture->getInternalFormat(m_target, m_level);
209 WebGLSharedObject* WebGLTextureAttachment::object() const
211 return m_texture->object() ? m_texture.get() : 0;
214 bool WebGLTextureAttachment::isSharedObject(WebGLSharedObject* object) const
216 return object == m_texture;
219 bool WebGLTextureAttachment::valid() const
221 return m_texture->object();
224 void WebGLTextureAttachment::onDetached(blink::WebGraphicsContext3D* context)
226 m_texture->onDetached(context);
229 void WebGLTextureAttachment::attach(blink::WebGraphicsContext3D* context, GLenum attachment)
231 Platform3DObject object = objectOrZero(m_texture.get());
232 context->framebufferTexture2D(GL_FRAMEBUFFER, attachment, m_target, object, m_level);
235 void WebGLTextureAttachment::unattach(blink::WebGraphicsContext3D* context, GLenum attachment)
237 if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
238 context->framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_target, 0, m_level);
239 context->framebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, m_target, 0, m_level);
241 context->framebufferTexture2D(GL_FRAMEBUFFER, attachment, m_target, 0, m_level);
245 GLenum WebGLTextureAttachment::type() const
247 return m_texture->getType(m_target, m_level);
250 bool isColorRenderable(GLenum internalformat)
252 switch (internalformat) {
262 } // anonymous namespace
264 WebGLFramebuffer::WebGLAttachment::WebGLAttachment()
268 WebGLFramebuffer::WebGLAttachment::~WebGLAttachment()
272 PassRefPtrWillBeRawPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContextBase* ctx)
274 return adoptRefWillBeNoop(new WebGLFramebuffer(ctx));
277 WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContextBase* ctx)
278 : WebGLContextObject(ctx)
279 , m_hasEverBeenBound(false)
281 ScriptWrappable::init(this);
282 setObject(ctx->webContext()->createFramebuffer());
285 WebGLFramebuffer::~WebGLFramebuffer()
287 // Delete the platform framebuffer resource. Explicit detachment
288 // is for the benefit of Oilpan, where the framebuffer object
289 // isn't detached when it and the WebGLRenderingContextBase object
290 // it is registered with are both finalized. Without Oilpan, the
291 // object will have been detached.
293 // To keep the code regular, the trivial detach()ment is always
295 detachAndDeleteObject();
298 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GLenum attachment, GLenum texTarget, WebGLTexture* texture, GLint level)
301 removeAttachmentFromBoundFramebuffer(attachment);
304 if (texture && texture->object()) {
305 m_attachments.add(attachment, WebGLTextureAttachment::create(texture, texTarget, level));
306 drawBuffersIfNecessary(false);
307 texture->onAttached();
311 void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GLenum attachment, WebGLRenderbuffer* renderbuffer)
314 removeAttachmentFromBoundFramebuffer(attachment);
317 if (renderbuffer && renderbuffer->object()) {
318 m_attachments.add(attachment, WebGLRenderbufferAttachment::create(renderbuffer));
319 drawBuffersIfNecessary(false);
320 renderbuffer->onAttached();
324 void WebGLFramebuffer::attach(GLenum attachment, GLenum attachmentPoint)
327 WebGLAttachment* attachmentObject = getAttachment(attachment);
328 if (attachmentObject)
329 attachmentObject->attach(context()->webContext(), attachmentPoint);
332 WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GLenum attachment) const
336 WebGLAttachment* attachmentObject = getAttachment(attachment);
337 return attachmentObject ? attachmentObject->object() : 0;
340 bool WebGLFramebuffer::isAttachmentComplete(WebGLAttachment* attachedObject, GLenum attachment, const char** reason) const
342 ASSERT(attachedObject && attachedObject->valid());
345 GLenum internalformat = attachedObject->format();
346 WebGLSharedObject* object = attachedObject->object();
347 ASSERT(object && (object->isTexture() || object->isRenderbuffer()));
349 if (attachment == GL_DEPTH_ATTACHMENT) {
350 if (object->isRenderbuffer()) {
351 if (internalformat != GL_DEPTH_COMPONENT16) {
352 *reason = "the internalformat of the attached renderbuffer is not DEPTH_COMPONENT16";
355 } else if (object->isTexture()) {
356 GLenum type = attachedObject->type();
357 if (!(context()->extensionEnabled(WebGLDepthTextureName) && internalformat == GL_DEPTH_COMPONENT
358 && (type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT))) {
359 *reason = "the attached texture is not a depth texture";
363 } else if (attachment == GL_STENCIL_ATTACHMENT) {
364 // Depend on the underlying GL drivers to check stencil textures
365 // and check renderbuffer type here only.
366 if (object->isRenderbuffer()) {
367 if (internalformat != GL_STENCIL_INDEX8) {
368 *reason = "the internalformat of the attached renderbuffer is not STENCIL_INDEX8";
372 } else if (attachment == GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL) {
373 if (object->isRenderbuffer()) {
374 if (internalformat != GL_DEPTH_STENCIL_OES) {
375 *reason = "the internalformat of the attached renderbuffer is not DEPTH_STENCIL";
378 } else if (object->isTexture()) {
379 GLenum type = attachedObject->type();
380 if (!(context()->extensionEnabled(WebGLDepthTextureName) && internalformat == GL_DEPTH_STENCIL_OES
381 && type == GL_UNSIGNED_INT_24_8_OES)) {
382 *reason = "the attached texture is not a DEPTH_STENCIL texture";
386 } else if (attachment == GL_COLOR_ATTACHMENT0
387 || (context()->extensionEnabled(WebGLDrawBuffersName) && attachment > GL_COLOR_ATTACHMENT0
388 && attachment < static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + context()->maxColorAttachments()))) {
389 if (object->isRenderbuffer()) {
390 if (!isColorRenderable(internalformat)) {
391 *reason = "the internalformat of the attached renderbuffer is not color-renderable";
394 } else if (object->isTexture()) {
395 GLenum type = attachedObject->type();
396 if (internalformat != GL_RGBA && internalformat != GL_RGB) {
397 *reason = "the internalformat of the attached texture is not color-renderable";
400 // TODO: WEBGL_color_buffer_float and EXT_color_buffer_half_float extensions have not been implemented in
401 // WebGL yet. It would be better to depend on the underlying GL drivers to check on rendering to floating point textures
402 // and add the check back to WebGL when above two extensions are implemented.
403 // Assume UNSIGNED_BYTE is renderable here without the need to explicitly check if GL_OES_rgb8_rgba8 extension is supported.
404 if (type != GL_UNSIGNED_BYTE
405 && type != GL_UNSIGNED_SHORT_5_6_5
406 && type != GL_UNSIGNED_SHORT_4_4_4_4
407 && type != GL_UNSIGNED_SHORT_5_5_5_1
408 && !(type == GL_FLOAT && context()->extensionEnabled(OESTextureFloatName))
409 && !(type == GL_HALF_FLOAT_OES && context()->extensionEnabled(OESTextureHalfFloatName))) {
410 *reason = "unsupported type: The attached texture is not supported to be rendered to";
415 *reason = "unknown framebuffer attachment point";
419 if (!attachedObject->width() || !attachedObject->height()) {
420 *reason = "attachment has a 0 dimension";
426 WebGLFramebuffer::WebGLAttachment* WebGLFramebuffer::getAttachment(GLenum attachment) const
428 const AttachmentMap::const_iterator it = m_attachments.find(attachment);
429 return (it != m_attachments.end()) ? it->value.get() : 0;
432 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GLenum attachment)
438 WebGLAttachment* attachmentObject = getAttachment(attachment);
439 if (attachmentObject) {
440 attachmentObject->onDetached(context()->webContext());
441 m_attachments.remove(attachment);
442 drawBuffersIfNecessary(false);
443 switch (attachment) {
444 case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL:
445 attach(GL_DEPTH_ATTACHMENT, GL_DEPTH_ATTACHMENT);
446 attach(GL_STENCIL_ATTACHMENT, GL_STENCIL_ATTACHMENT);
448 case GL_DEPTH_ATTACHMENT:
449 attach(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL, GL_DEPTH_ATTACHMENT);
451 case GL_STENCIL_ATTACHMENT:
452 attach(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL, GL_STENCIL_ATTACHMENT);
458 void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLSharedObject* attachment)
466 bool checkMore = true;
469 for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
470 WebGLAttachment* attachmentObject = it->value.get();
471 if (attachmentObject->isSharedObject(attachment)) {
472 GLenum attachmentType = it->key;
473 attachmentObject->unattach(context()->webContext(), attachmentType);
474 removeAttachmentFromBoundFramebuffer(attachmentType);
482 GLenum WebGLFramebuffer::colorBufferFormat() const
486 WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0);
489 return attachment->format();
492 GLenum WebGLFramebuffer::checkStatus(const char** reason) const
495 GLsizei width = 0, height = 0;
496 bool haveDepth = false;
497 bool haveStencil = false;
498 bool haveDepthStencil = false;
499 for (AttachmentMap::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) {
500 WebGLAttachment* attachment = it->value.get();
501 if (!isAttachmentComplete(attachment, it->key, reason))
502 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
503 if (!attachment->valid()) {
504 *reason = "attachment is not valid";
505 return GL_FRAMEBUFFER_UNSUPPORTED;
507 if (!attachment->format()) {
508 *reason = "attachment is an unsupported format";
509 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
512 case GL_DEPTH_ATTACHMENT:
515 case GL_STENCIL_ATTACHMENT:
518 case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL:
519 haveDepthStencil = true;
523 width = attachment->width();
524 height = attachment->height();
526 if (width != attachment->width() || height != attachment->height()) {
527 *reason = "attachments do not have the same dimensions";
528 return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
534 *reason = "no attachments";
535 return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
537 if (!width || !height) {
538 *reason = "framebuffer has a 0 dimension";
539 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
541 // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments.
542 if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) {
543 *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments";
544 return GL_FRAMEBUFFER_UNSUPPORTED;
546 return GL_FRAMEBUFFER_COMPLETE;
549 bool WebGLFramebuffer::onAccess(blink::WebGraphicsContext3D* context3d, const char** reason)
551 if (checkStatus(reason) != GL_FRAMEBUFFER_COMPLETE)
556 bool WebGLFramebuffer::hasStencilBuffer() const
558 WebGLAttachment* attachment = getAttachment(GL_STENCIL_ATTACHMENT);
560 attachment = getAttachment(GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL);
561 return attachment && attachment->valid();
564 void WebGLFramebuffer::deleteObjectImpl(blink::WebGraphicsContext3D* context3d, Platform3DObject object)
567 // With Oilpan, both the AttachmentMap and its WebGLAttachment objects are
568 // GCed objects and cannot be accessed, as they may have been finalized
569 // already during the same GC sweep.
571 // The WebGLAttachment-derived classes instead handle detachment
572 // on their own when finalizing, so the explicit notification is
574 for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
575 it->value->onDetached(context3d);
578 context3d->deleteFramebuffer(object);
581 bool WebGLFramebuffer::isBound() const
583 return (context()->m_framebufferBinding.get() == this);
586 void WebGLFramebuffer::drawBuffers(const Vector<GLenum>& bufs)
588 m_drawBuffers = bufs;
589 m_filteredDrawBuffers.resize(m_drawBuffers.size());
590 for (size_t i = 0; i < m_filteredDrawBuffers.size(); ++i)
591 m_filteredDrawBuffers[i] = GL_NONE;
592 drawBuffersIfNecessary(true);
595 void WebGLFramebuffer::drawBuffersIfNecessary(bool force)
597 if (!context()->extensionEnabled(WebGLDrawBuffersName))
600 // This filtering works around graphics driver bugs on Mac OS X.
601 for (size_t i = 0; i < m_drawBuffers.size(); ++i) {
602 if (m_drawBuffers[i] != GL_NONE && getAttachment(m_drawBuffers[i])) {
603 if (m_filteredDrawBuffers[i] != m_drawBuffers[i]) {
604 m_filteredDrawBuffers[i] = m_drawBuffers[i];
608 if (m_filteredDrawBuffers[i] != GL_NONE) {
609 m_filteredDrawBuffers[i] = GL_NONE;
615 context()->webContext()->drawBuffersEXT(
616 m_filteredDrawBuffers.size(), m_filteredDrawBuffers.data());
620 GLenum WebGLFramebuffer::getDrawBuffer(GLenum drawBuffer)
622 int index = static_cast<int>(drawBuffer - GL_DRAW_BUFFER0_EXT);
624 if (index < static_cast<int>(m_drawBuffers.size()))
625 return m_drawBuffers[index];
626 if (drawBuffer == GL_DRAW_BUFFER0_EXT)
627 return GL_COLOR_ATTACHMENT0;
631 void WebGLFramebuffer::trace(Visitor* visitor)
634 visitor->trace(m_attachments);
636 WebGLContextObject::trace(visitor);