diff options
Diffstat (limited to 'dom/canvas/WebGL2ContextFramebuffers.cpp')
-rw-r--r-- | dom/canvas/WebGL2ContextFramebuffers.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/dom/canvas/WebGL2ContextFramebuffers.cpp b/dom/canvas/WebGL2ContextFramebuffers.cpp new file mode 100644 index 000000000..c22bb9190 --- /dev/null +++ b/dom/canvas/WebGL2ContextFramebuffers.cpp @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebGL2Context.h" + +#include "GLContext.h" +#include "GLScreenBuffer.h" +#include "WebGLContextUtils.h" +#include "WebGLFormats.h" +#include "WebGLFramebuffer.h" + +namespace mozilla { + +void +WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + if (IsContextLost()) + return; + + const GLbitfield validBits = LOCAL_GL_COLOR_BUFFER_BIT | + LOCAL_GL_DEPTH_BUFFER_BIT | + LOCAL_GL_STENCIL_BUFFER_BIT; + if ((mask | validBits) != validBits) { + ErrorInvalidValue("blitFramebuffer: Invalid bit set in mask."); + return; + } + + switch (filter) { + case LOCAL_GL_NEAREST: + case LOCAL_GL_LINEAR: + break; + default: + ErrorInvalidEnumInfo("blitFramebuffer: Bad `filter`:", filter); + return; + } + + //// + + const auto& readFB = mBoundReadFramebuffer; + if (readFB && + !readFB->ValidateAndInitAttachments("blitFramebuffer's READ_FRAMEBUFFER")) + { + return; + } + + const auto& drawFB = mBoundDrawFramebuffer; + if (drawFB && + !drawFB->ValidateAndInitAttachments("blitFramebuffer's DRAW_FRAMEBUFFER")) + { + return; + } + + //// + + if (!mBoundReadFramebuffer) { + ClearBackbufferIfNeeded(); + } + + WebGLFramebuffer::BlitFramebuffer(this, + readFB, srcX0, srcY0, srcX1, srcY1, + drawFB, dstX0, dstY0, dstX1, dstY1, + mask, filter); +} + +void +WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment, + WebGLTexture* texture, GLint level, GLint layer) +{ + const char funcName[] = "framebufferTextureLayer"; + if (IsContextLost()) + return; + + if (!ValidateFramebufferTarget(target, funcName)) + return; + + WebGLFramebuffer* fb; + switch (target) { + case LOCAL_GL_FRAMEBUFFER: + case LOCAL_GL_DRAW_FRAMEBUFFER: + fb = mBoundDrawFramebuffer; + break; + + case LOCAL_GL_READ_FRAMEBUFFER: + fb = mBoundReadFramebuffer; + break; + + default: + MOZ_CRASH("GFX: Bad target."); + } + + if (!fb) + return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName); + + fb->FramebufferTextureLayer(funcName, attachment, texture, level, layer); +} + +JS::Value +WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx, + GLenum target, + GLenum attachment, + GLenum pname, + ErrorResult& out_error) +{ + return WebGLContext::GetFramebufferAttachmentParameter(cx, target, attachment, pname, + out_error); +} + +//// + +static bool +ValidateBackbufferAttachmentEnum(WebGLContext* webgl, const char* funcName, + GLenum attachment) +{ + switch (attachment) { + case LOCAL_GL_COLOR: + case LOCAL_GL_DEPTH: + case LOCAL_GL_STENCIL: + return true; + + default: + webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", + funcName, attachment); + return false; + } +} + +static bool +ValidateFramebufferAttachmentEnum(WebGLContext* webgl, const char* funcName, + GLenum attachment) +{ + switch (attachment) { + case LOCAL_GL_DEPTH_ATTACHMENT: + case LOCAL_GL_STENCIL_ATTACHMENT: + case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: + return true; + } + + if (attachment < LOCAL_GL_COLOR_ATTACHMENT0) { + webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", + funcName, attachment); + return false; + } + + if (attachment > webgl->LastColorAttachmentEnum()) { + // That these errors have different types is ridiculous. + webgl->ErrorInvalidOperation("%s: Too-large LOCAL_GL_COLOR_ATTACHMENTn.", + funcName); + return false; + } + + return true; +} + +bool +WebGLContext::ValidateInvalidateFramebuffer(const char* funcName, GLenum target, + const dom::Sequence<GLenum>& attachments, + ErrorResult* const out_rv, + std::vector<GLenum>* const scopedVector, + GLsizei* const out_glNumAttachments, + const GLenum** const out_glAttachments) +{ + if (IsContextLost()) + return false; + + gl->MakeCurrent(); + + if (!ValidateFramebufferTarget(target, funcName)) + return false; + + const WebGLFramebuffer* fb; + bool isDefaultFB; + switch (target) { + case LOCAL_GL_FRAMEBUFFER: + case LOCAL_GL_DRAW_FRAMEBUFFER: + fb = mBoundDrawFramebuffer; + isDefaultFB = gl->Screen()->IsDrawFramebufferDefault(); + break; + + case LOCAL_GL_READ_FRAMEBUFFER: + fb = mBoundReadFramebuffer; + isDefaultFB = gl->Screen()->IsReadFramebufferDefault(); + break; + + default: + MOZ_CRASH("GFX: Bad target."); + } + + *out_glNumAttachments = attachments.Length(); + *out_glAttachments = attachments.Elements(); + + if (fb) { + for (const auto& attachment : attachments) { + if (!ValidateFramebufferAttachmentEnum(this, funcName, attachment)) + return false; + } + } else { + for (const auto& attachment : attachments) { + if (!ValidateBackbufferAttachmentEnum(this, funcName, attachment)) + return false; + } + + if (!isDefaultFB) { + MOZ_ASSERT(scopedVector->empty()); + scopedVector->reserve(attachments.Length()); + for (const auto& attachment : attachments) { + switch (attachment) { + case LOCAL_GL_COLOR: + scopedVector->push_back(LOCAL_GL_COLOR_ATTACHMENT0); + break; + + case LOCAL_GL_DEPTH: + scopedVector->push_back(LOCAL_GL_DEPTH_ATTACHMENT); + break; + + case LOCAL_GL_STENCIL: + scopedVector->push_back(LOCAL_GL_STENCIL_ATTACHMENT); + break; + + default: + MOZ_CRASH(); + } + } + *out_glNumAttachments = scopedVector->size(); + *out_glAttachments = scopedVector->data(); + } + } + + //// + + if (!fb) { + ClearBackbufferIfNeeded(); + + // Don't do more validation after these. + Invalidate(); + mShouldPresent = true; + } + + return true; +} + +void +WebGL2Context::InvalidateFramebuffer(GLenum target, + const dom::Sequence<GLenum>& attachments, + ErrorResult& rv) +{ + const char funcName[] = "invalidateSubFramebuffer"; + + std::vector<GLenum> scopedVector; + GLsizei glNumAttachments; + const GLenum* glAttachments; + if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector, + &glNumAttachments, &glAttachments)) + { + return; + } + + //// + + // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer. + const bool useFBInvalidation = (mAllowFBInvalidation && + gl->IsSupported(gl::GLFeature::invalidate_framebuffer)); + if (useFBInvalidation) { + gl->fInvalidateFramebuffer(target, glNumAttachments, glAttachments); + return; + } + + // Use clear instead? + // No-op for now. +} + +void +WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments, + GLint x, GLint y, GLsizei width, GLsizei height, + ErrorResult& rv) +{ + const char funcName[] = "invalidateSubFramebuffer"; + + if (!ValidateNonNegative(funcName, "width", width) || + !ValidateNonNegative(funcName, "height", height)) + { + return; + } + + std::vector<GLenum> scopedVector; + GLsizei glNumAttachments; + const GLenum* glAttachments; + if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector, + &glNumAttachments, &glAttachments)) + { + return; + } + + //// + + // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer. + const bool useFBInvalidation = (mAllowFBInvalidation && + gl->IsSupported(gl::GLFeature::invalidate_framebuffer)); + if (useFBInvalidation) { + gl->fInvalidateSubFramebuffer(target, glNumAttachments, glAttachments, x, y, + width, height); + return; + } + + // Use clear instead? + // No-op for now. +} + +void +WebGL2Context::ReadBuffer(GLenum mode) +{ + const char funcName[] = "readBuffer"; + if (IsContextLost()) + return; + + if (mBoundReadFramebuffer) { + mBoundReadFramebuffer->ReadBuffer(funcName, mode); + return; + } + + // Operating on the default framebuffer. + if (mode != LOCAL_GL_NONE && + mode != LOCAL_GL_BACK) + { + nsCString enumName; + EnumName(mode, &enumName); + ErrorInvalidOperation("%s: If READ_FRAMEBUFFER is null, `mode` must be BACK or" + " NONE. Was %s.", + funcName, enumName.BeginReading()); + return; + } + + gl->Screen()->SetReadBuffer(mode); +} + +} // namespace mozilla |