summaryrefslogtreecommitdiffstats
path: root/dom/canvas/WebGL2ContextFramebuffers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/WebGL2ContextFramebuffers.cpp')
-rw-r--r--dom/canvas/WebGL2ContextFramebuffers.cpp269
1 files changed, 269 insertions, 0 deletions
diff --git a/dom/canvas/WebGL2ContextFramebuffers.cpp b/dom/canvas/WebGL2ContextFramebuffers.cpp
new file mode 100644
index 0000000000..7056c83d03
--- /dev/null
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "mozilla/CheckedInt.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) {
+ const FuncScope funcScope(*this, "blitFramebuffer");
+ 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("Invalid bit set in mask.");
+ return;
+ }
+
+ switch (filter) {
+ case LOCAL_GL_NEAREST:
+ case LOCAL_GL_LINEAR:
+ break;
+ default:
+ ErrorInvalidEnumInfo("filter", filter);
+ return;
+ }
+
+ // --
+
+ const auto fnLikelyOverflow = [](GLint p0, GLint p1) {
+ auto checked = CheckedInt<GLint>(p1) - p0;
+ checked = -checked; // And check the negation!
+ return !checked.isValid();
+ };
+
+ if (fnLikelyOverflow(srcX0, srcX1) || fnLikelyOverflow(srcY0, srcY1) ||
+ fnLikelyOverflow(dstX0, dstX1) || fnLikelyOverflow(dstY0, dstY1)) {
+ ErrorInvalidValue("Likely-to-overflow large ranges are forbidden.");
+ return;
+ }
+
+ // --
+
+ if (!ValidateAndInitFB(mBoundReadFramebuffer) ||
+ !ValidateAndInitFB(mBoundDrawFramebuffer)) {
+ return;
+ }
+
+ DoBindFB(mBoundReadFramebuffer, LOCAL_GL_READ_FRAMEBUFFER);
+ DoBindFB(mBoundDrawFramebuffer, LOCAL_GL_DRAW_FRAMEBUFFER);
+
+ WebGLFramebuffer::BlitFramebuffer(this, srcX0, srcY0, srcX1, srcY1, dstX0,
+ dstY0, dstX1, dstY1, mask, filter);
+}
+
+////
+
+static bool ValidateBackbufferAttachmentEnum(WebGLContext* webgl,
+ GLenum attachment) {
+ switch (attachment) {
+ case LOCAL_GL_COLOR:
+ case LOCAL_GL_DEPTH:
+ case LOCAL_GL_STENCIL:
+ return true;
+
+ default:
+ webgl->ErrorInvalidEnumInfo("attachment", attachment);
+ return false;
+ }
+}
+
+static bool ValidateFramebufferAttachmentEnum(WebGLContext* webgl,
+ 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->ErrorInvalidEnumInfo("attachment", attachment);
+ return false;
+ }
+
+ if (attachment > webgl->LastColorAttachmentEnum()) {
+ // That these errors have different types is ridiculous.
+ webgl->ErrorInvalidOperation("Too-large LOCAL_GL_COLOR_ATTACHMENTn.");
+ return false;
+ }
+
+ return true;
+}
+
+bool WebGLContext::ValidateInvalidateFramebuffer(
+ GLenum target, const Range<const GLenum>& attachments,
+ std::vector<GLenum>* const scopedVector,
+ GLsizei* const out_glNumAttachments,
+ const GLenum** const out_glAttachments) {
+ if (IsContextLost()) return false;
+
+ if (!ValidateFramebufferTarget(target)) return false;
+
+ const WebGLFramebuffer* fb;
+ bool isDefaultFB = false;
+ 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) {
+ const auto fbStatus = fb->CheckFramebufferStatus();
+ if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+ return false; // Not an error, but don't run forward to driver either.
+ } else {
+ if (!EnsureDefaultFB()) return false;
+ }
+ DoBindFB(fb, target);
+
+ *out_glNumAttachments = attachments.length();
+ *out_glAttachments = attachments.begin().get();
+
+ if (fb) {
+ for (const auto& attachment : attachments) {
+ if (!ValidateFramebufferAttachmentEnum(this, attachment)) return false;
+ }
+ } else {
+ for (const auto& attachment : attachments) {
+ if (!ValidateBackbufferAttachmentEnum(this, 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();
+ }
+ }
+
+ ////
+
+ return true;
+}
+
+void WebGL2Context::InvalidateFramebuffer(
+ GLenum target, const Range<const GLenum>& attachments) {
+ const FuncScope funcScope(*this, "invalidateFramebuffer");
+
+ std::vector<GLenum> scopedVector;
+ GLsizei glNumAttachments;
+ const GLenum* glAttachments;
+ if (!ValidateInvalidateFramebuffer(target, attachments, &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 Range<const GLenum>& attachments, GLint x, GLint y,
+ GLsizei width, GLsizei height) {
+ const FuncScope funcScope(*this, "invalidateSubFramebuffer");
+
+ std::vector<GLenum> scopedVector;
+ GLsizei glNumAttachments;
+ const GLenum* glAttachments;
+ if (!ValidateInvalidateFramebuffer(target, attachments, &scopedVector,
+ &glNumAttachments, &glAttachments)) {
+ return;
+ }
+
+ if (!ValidateNonNegative("width", width) ||
+ !ValidateNonNegative("height", height)) {
+ 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 FuncScope funcScope(*this, "readBuffer");
+ if (IsContextLost()) return;
+
+ if (mBoundReadFramebuffer) {
+ mBoundReadFramebuffer->ReadBuffer(mode);
+ return;
+ }
+
+ // Operating on the default framebuffer.
+ if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK) {
+ nsCString enumName;
+ EnumName(mode, &enumName);
+ ErrorInvalidOperation(
+ "If READ_FRAMEBUFFER is null, `mode` must be BACK or"
+ " NONE. Was %s.",
+ enumName.BeginReading());
+ return;
+ }
+
+ mDefaultFB_ReadBuffer = mode;
+}
+
+} // namespace mozilla