WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GC3Denum attachment) const { if (!object()) return 0; WebGLAttachment* attachmentObject = getAttachment(attachment); return attachmentObject ? attachmentObject->getObject() : 0; }
void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GC3Denum attachment) { ASSERT(isBound()); if (!object()) return; WebGLAttachment* attachmentObject = getAttachment(attachment); if (attachmentObject) { attachmentObject->onDetached(context()->graphicsContext3D()); m_attachments.remove(attachment); drawBuffersIfNecessary(false); switch (attachment) { case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: attach(GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::DEPTH_ATTACHMENT); attach(GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::STENCIL_ATTACHMENT); break; case GraphicsContext3D::DEPTH_ATTACHMENT: attach(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, GraphicsContext3D::DEPTH_ATTACHMENT); break; case GraphicsContext3D::STENCIL_ATTACHMENT: attach(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, GraphicsContext3D::STENCIL_ATTACHMENT); break; } } }
bool WebGLFramebuffer::hasStencilBuffer() const { WebGLAttachment* attachment = getAttachment(GL_STENCIL_ATTACHMENT); if (!attachment) attachment = getAttachment(GL_DEPTH_STENCIL_ATTACHMENT); return attachment && attachment->valid(); }
void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GLenum target, GLenum attachment) { ASSERT(isBound(target)); if (!m_object) return; WebGLAttachment* attachmentObject = getAttachment(attachment); if (attachmentObject) { attachmentObject->onDetached(context()->webContext()); m_attachments.remove(attachment); drawBuffersIfNecessary(false); switch (attachment) { case GL_DEPTH_STENCIL_ATTACHMENT: attach(target, GL_DEPTH_ATTACHMENT, GL_DEPTH_ATTACHMENT); attach(target, GL_STENCIL_ATTACHMENT, GL_STENCIL_ATTACHMENT); break; case GL_DEPTH_ATTACHMENT: attach(target, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH_ATTACHMENT); break; case GL_STENCIL_ATTACHMENT: attach(target, GL_DEPTH_STENCIL_ATTACHMENT, GL_STENCIL_ATTACHMENT); break; } } }
WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GLenum attachment) const { if (!m_object) return nullptr; WebGLAttachment* attachmentObject = getAttachment(attachment); return attachmentObject ? attachmentObject->object() : nullptr; }
void WebGLFramebuffer::attach(GLenum target, GLenum attachment, GLenum attachmentPoint) { ASSERT(isBound(target)); WebGLAttachment* attachmentObject = getAttachment(attachment); if (attachmentObject) attachmentObject->attach(context()->webContext(), target, attachmentPoint); }
void WebGLFramebuffer::attach(GC3Denum attachment, GC3Denum attachmentPoint) { ASSERT(isBound()); WebGLAttachment* attachmentObject = getAttachment(attachment); if (attachmentObject) attachmentObject->attach(context()->graphicsContext3D(), attachmentPoint); }
bool WebGLFramebuffer::hasStencilBuffer() const { WebGLAttachment* attachment = getAttachment(GraphicsContext3D::STENCIL_ATTACHMENT); if (!attachment) attachment = getAttachment(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT); return attachment && attachment->isValid(); }
GLenum WebGLFramebuffer::colorBufferFormat() const { if (!m_object) return 0; WebGLAttachment* attachment = getAttachment(GL_COLOR_ATTACHMENT0); if (!attachment) return 0; return attachment->format(); }
GC3Denum WebGLFramebuffer::getColorBufferFormat() const { if (!object()) return 0; WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0); if (!attachment) return 0; return attachment->getFormat(); }
bool WebGLFramebuffer::getReadBufferFormatAndType(GLenum* format, GLenum* type) const { if (m_readBuffer == GL_NONE) return false; WebGLAttachment* image = getAttachment(m_readBuffer); if (!image) return false; if (format) *format = image->format(); if (type) *type = image->type(); return true; }
GC3Denum WebGLFramebuffer::checkStatus(const char** reason) const { unsigned int count = 0; GC3Dsizei width = 0, height = 0; bool haveDepth = false; bool haveStencil = false; bool haveDepthStencil = false; for (AttachmentMap::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) { WebGLAttachment* attachment = it->value.get(); if (!isAttachmentComplete(attachment, it->key, reason)) return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; if (!attachment->isValid()) { *reason = "attachment is not valid"; return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; } if (!attachment->getFormat()) { *reason = "attachment is an unsupported format"; return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } switch (it->key) { case GraphicsContext3D::DEPTH_ATTACHMENT: haveDepth = true; break; case GraphicsContext3D::STENCIL_ATTACHMENT: haveStencil = true; break; case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: haveDepthStencil = true; break; } if (!count) { width = attachment->getWidth(); height = attachment->getHeight(); } else { if (width != attachment->getWidth() || height != attachment->getHeight()) { *reason = "attachments do not have the same dimensions"; return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS; } } ++count; } if (!count) { *reason = "no attachments"; return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; } if (!width || !height) { *reason = "framebuffer has a 0 dimension"; return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments. if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) { *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments"; return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; } return GraphicsContext3D::FRAMEBUFFER_COMPLETE; }
GLenum WebGLFramebuffer::checkStatus(const char** reason) const { unsigned count = 0; GLsizei width = 0, height = 0; bool haveDepth = false; bool haveStencil = false; bool haveDepthStencil = false; for (const auto& it : m_attachments) { WebGLAttachment* attachment = it.value.get(); if (!isAttachmentComplete(attachment, it.key, reason)) return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; if (!attachment->valid()) { *reason = "attachment is not valid"; return GL_FRAMEBUFFER_UNSUPPORTED; } if (!attachment->format()) { *reason = "attachment is an unsupported format"; return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } switch (it.key) { case GL_DEPTH_ATTACHMENT: haveDepth = true; break; case GL_STENCIL_ATTACHMENT: haveStencil = true; break; case GC3D_DEPTH_STENCIL_ATTACHMENT_WEBGL: haveDepthStencil = true; break; } if (!count) { width = attachment->width(); height = attachment->height(); } else { if (width != attachment->width() || height != attachment->height()) { *reason = "attachments do not have the same dimensions"; return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; } } ++count; } if (!count) { *reason = "no attachments"; return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; } if (!width || !height) { *reason = "framebuffer has a 0 dimension"; return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments. if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) { *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments"; return GL_FRAMEBUFFER_UNSUPPORTED; } return GL_FRAMEBUFFER_COMPLETE; }
void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GLenum target, WebGLSharedObject* attachment) { ASSERT(isBound(target)); if (!m_object) return; if (!attachment) return; bool checkMore = true; while (checkMore) { checkMore = false; for (const auto& it : m_attachments) { WebGLAttachment* attachmentObject = it.value.get(); if (attachmentObject->isSharedObject(attachment)) { GLenum attachmentType = it.key; attachmentObject->unattach(context()->webContext(), target, attachmentType); removeAttachmentFromBoundFramebuffer(target, attachmentType); checkMore = true; break; } } } }
void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLSharedObject* attachment) { ASSERT(isBound()); if (!object()) return; if (!attachment) return; bool checkMore = true; while (checkMore) { checkMore = false; for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) { WebGLAttachment* attachmentObject = it->value.get(); if (attachmentObject->isSharedObject(attachment)) { GC3Denum attachmentType = it->key; attachmentObject->unattach(context()->graphicsContext3D(), attachmentType); removeAttachmentFromBoundFramebuffer(attachmentType); checkMore = true; break; } } } }
GLenum WebGLFramebuffer::checkStatus(const char** reason) const { unsigned count = 0; GLsizei width = 0, height = 0, depth = 0; WebGLAttachment* depthAttachment = nullptr; WebGLAttachment* stencilAttachment = nullptr; WebGLAttachment* depthStencilAttachment = nullptr; bool isWebGL2OrHigher = context()->isWebGL2OrHigher(); for (const auto& it : m_attachments) { WebGLAttachment* attachment = it.value.get(); if (!isAttachmentComplete(attachment, it.key, reason)) return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; if (!attachment->valid()) { *reason = "attachment is not valid"; return GL_FRAMEBUFFER_UNSUPPORTED; } if (!attachment->format()) { *reason = "attachment is an unsupported format"; return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } switch (it.key) { case GL_DEPTH_ATTACHMENT: depthAttachment = attachment; break; case GL_STENCIL_ATTACHMENT: stencilAttachment = attachment; break; case GL_DEPTH_STENCIL_ATTACHMENT: depthStencilAttachment = attachment; break; } // Note: In GLES 3, images for a framebuffer need not to have the same dimensions to be framebuffer complete. // However, in Direct3D 11, on top of which OpenGL ES 3 behavior is emulated in Windows, all render targets // must have the same size in all dimensions. In order to have consistent WebGL 2 behaviors across platforms, // we generate FRAMEBUFFER_INCOMPLETE_DIMENSIONS in this situation. if (!count) { width = attachment->width(); height = attachment->height(); depth = attachment->depth(); } else { if (width != attachment->width() || height != attachment->height() || depth != attachment->depth()) { *reason = "attachments do not have the same dimensions"; return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; } } ++count; } if (!count) { *reason = "no attachments"; return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; } // WebGL 1 specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments. if (!isWebGL2OrHigher && ((depthStencilAttachment && (depthAttachment || stencilAttachment)) || (depthAttachment && stencilAttachment))) { *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments"; return GL_FRAMEBUFFER_UNSUPPORTED; } if (isWebGL2OrHigher && (depthAttachment && stencilAttachment && depthAttachment->object() != stencilAttachment->object())) { *reason = "both DEPTH/STENCIL attachments are present and not the same image"; return GL_FRAMEBUFFER_UNSUPPORTED; } return GL_FRAMEBUFFER_COMPLETE; }
GLenum WebGLFramebuffer::checkStatus(const char** reason) const { unsigned count = 0; GLsizei width = 0, height = 0; WebGLAttachment* depthAttachment = nullptr; WebGLAttachment* stencilAttachment = nullptr; WebGLAttachment* depthStencilAttachment = nullptr; bool isWebGL2OrHigher = context()->isWebGL2OrHigher(); for (const auto& it : m_attachments) { WebGLAttachment* attachment = it.value.get(); if (!isAttachmentComplete(attachment, it.key, reason)) return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; if (!attachment->valid()) { *reason = "attachment is not valid"; return GL_FRAMEBUFFER_UNSUPPORTED; } if (!attachment->format()) { *reason = "attachment is an unsupported format"; return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } switch (it.key) { case GL_DEPTH_ATTACHMENT: depthAttachment = attachment; break; case GL_STENCIL_ATTACHMENT: stencilAttachment = attachment; break; case GL_DEPTH_STENCIL_ATTACHMENT: depthStencilAttachment = attachment; break; } if (!isWebGL2OrHigher) { if (!count) { width = attachment->width(); height = attachment->height(); } else { if (width != attachment->width() || height != attachment->height()) { *reason = "attachments do not have the same dimensions"; return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; } } } ++count; } if (!count) { *reason = "no attachments"; return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; } // WebGL 1 specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments. if (!isWebGL2OrHigher && ((depthStencilAttachment && (depthAttachment || stencilAttachment)) || (depthAttachment && stencilAttachment))) { *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments"; return GL_FRAMEBUFFER_UNSUPPORTED; } if (isWebGL2OrHigher && (depthAttachment && stencilAttachment && depthAttachment->object() != stencilAttachment->object())) { *reason = "both DEPTH/STENCIL attachments are present and not the same image"; return GL_FRAMEBUFFER_UNSUPPORTED; } return GL_FRAMEBUFFER_COMPLETE; }
bool WebGLFramebuffer::initializeAttachments(GraphicsContext3D* g3d, const char** reason) { ASSERT(object()); GC3Dbitfield mask = 0; for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) { GC3Denum attachmentType = it->key; WebGLAttachment* attachment = it->value.get(); if (!attachment->isInitialized()) mask |= GraphicsContext3D::getClearBitsByAttachmentType(attachmentType); } if (!mask) return true; // We only clear un-initialized renderbuffers when they are ready to be // read, i.e., when the framebuffer is complete. if (g3d->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { *reason = "framebuffer not complete"; return false; } bool initColor = mask & GraphicsContext3D::COLOR_BUFFER_BIT; bool initDepth = mask & GraphicsContext3D::DEPTH_BUFFER_BIT; bool initStencil = mask & GraphicsContext3D::STENCIL_BUFFER_BIT; GC3Dfloat colorClearValue[] = {0, 0, 0, 0}, depthClearValue = 0; GC3Dint stencilClearValue = 0; GC3Dboolean colorMask[] = {0, 0, 0, 0}, depthMask = 0; GC3Duint stencilMask = 0xffffffff; GC3Dboolean isScissorEnabled = 0; GC3Dboolean isDitherEnabled = 0; if (initColor) { g3d->getFloatv(GraphicsContext3D::COLOR_CLEAR_VALUE, colorClearValue); g3d->getBooleanv(GraphicsContext3D::COLOR_WRITEMASK, colorMask); g3d->clearColor(0, 0, 0, 0); g3d->colorMask(true, true, true, true); } if (initDepth) { g3d->getFloatv(GraphicsContext3D::DEPTH_CLEAR_VALUE, &depthClearValue); g3d->getBooleanv(GraphicsContext3D::DEPTH_WRITEMASK, &depthMask); g3d->clearDepth(1.0f); g3d->depthMask(true); } if (initStencil) { g3d->getIntegerv(GraphicsContext3D::STENCIL_CLEAR_VALUE, &stencilClearValue); g3d->getIntegerv(GraphicsContext3D::STENCIL_WRITEMASK, reinterpret_cast<GC3Dint*>(&stencilMask)); g3d->clearStencil(0); g3d->stencilMask(0xffffffff); } isScissorEnabled = g3d->isEnabled(GraphicsContext3D::SCISSOR_TEST); g3d->disable(GraphicsContext3D::SCISSOR_TEST); isDitherEnabled = g3d->isEnabled(GraphicsContext3D::DITHER); g3d->disable(GraphicsContext3D::DITHER); g3d->clear(mask); if (initColor) { g3d->clearColor(colorClearValue[0], colorClearValue[1], colorClearValue[2], colorClearValue[3]); g3d->colorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); } if (initDepth) { g3d->clearDepth(depthClearValue); g3d->depthMask(depthMask); } if (initStencil) { g3d->clearStencil(stencilClearValue); g3d->stencilMask(stencilMask); } if (isScissorEnabled) g3d->enable(GraphicsContext3D::SCISSOR_TEST); else g3d->disable(GraphicsContext3D::SCISSOR_TEST); if (isDitherEnabled) g3d->enable(GraphicsContext3D::DITHER); else g3d->disable(GraphicsContext3D::DITHER); for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) { GC3Denum attachmentType = it->key; WebGLAttachment* attachment = it->value.get(); GC3Dbitfield bits = GraphicsContext3D::getClearBitsByAttachmentType(attachmentType); if (bits & mask) attachment->setInitialized(); } return true; }