summaryrefslogtreecommitdiffstats
path: root/dom/canvas/WebGLContextState.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/WebGLContextState.cpp')
-rw-r--r--dom/canvas/WebGLContextState.cpp484
1 files changed, 484 insertions, 0 deletions
diff --git a/dom/canvas/WebGLContextState.cpp b/dom/canvas/WebGLContextState.cpp
new file mode 100644
index 0000000000..e29c69656a
--- /dev/null
+++ b/dom/canvas/WebGLContextState.cpp
@@ -0,0 +1,484 @@
+/* -*- 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 "WebGLContext.h"
+
+#include "GLContext.h"
+#include "GLScreenBuffer.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Preferences.h"
+#include "MozFramebuffer.h"
+#include "nsString.h"
+#include "WebGLBuffer.h"
+#include "WebGLContextUtils.h"
+#include "WebGLFramebuffer.h"
+#include "WebGLProgram.h"
+#include "WebGLRenderbuffer.h"
+#include "WebGLShader.h"
+#include "WebGLTexture.h"
+#include "WebGLVertexArray.h"
+
+namespace mozilla {
+
+void WebGLContext::SetEnabled(const GLenum cap, const Maybe<GLuint> i,
+ const bool enabled) {
+ const FuncScope funcScope(*this, "enable(i)/disable(i)");
+ if (IsContextLost()) return;
+
+ if (!ValidateCapabilityEnum(cap)) return;
+
+ if (i) {
+ if (cap != LOCAL_GL_BLEND) {
+ ErrorInvalidEnumArg("cap", cap);
+ return;
+ }
+
+ const auto limit = MaxValidDrawBuffers();
+ if (*i >= limit) {
+ ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i,
+ "MAX_DRAW_BUFFERS", limit);
+ return;
+ }
+ }
+
+ const auto slot = GetStateTrackingSlot(cap, i ? *i : 0);
+ if (slot) {
+ *slot = enabled;
+ } else if (cap == LOCAL_GL_BLEND) {
+ if (i) {
+ mBlendEnabled[*i] = enabled;
+ } else {
+ if (enabled) {
+ mBlendEnabled.set();
+ } else {
+ mBlendEnabled.reset();
+ }
+ }
+ }
+
+ switch (cap) {
+ case LOCAL_GL_DEPTH_TEST:
+ case LOCAL_GL_STENCIL_TEST:
+ break; // Lazily applied, so don't tell GL yet or we will desync.
+
+ default:
+ // Non-lazy caps.
+ if (i) {
+ if (enabled) {
+ gl->fEnablei(cap, *i);
+ } else {
+ gl->fDisablei(cap, *i);
+ }
+ } else {
+ gl->SetEnabled(cap, enabled);
+ }
+ break;
+ }
+}
+
+bool WebGLContext::GetStencilBits(GLint* const out_stencilBits) const {
+ *out_stencilBits = 0;
+ if (mBoundDrawFramebuffer) {
+ if (!mBoundDrawFramebuffer->IsCheckFramebufferStatusComplete()) {
+ // Error, we don't know which stencil buffer's bits to use
+ ErrorInvalidFramebufferOperation(
+ "getParameter: framebuffer has two stencil buffers bound");
+ return false;
+ }
+
+ if (mBoundDrawFramebuffer->StencilAttachment().HasAttachment() ||
+ mBoundDrawFramebuffer->DepthStencilAttachment().HasAttachment()) {
+ *out_stencilBits = 8;
+ }
+ } else if (mOptions.stencil) {
+ *out_stencilBits = 8;
+ }
+
+ return true;
+}
+
+Maybe<double> WebGLContext::GetParameter(const GLenum pname) {
+ const FuncScope funcScope(*this, "getParameter");
+ if (IsContextLost()) return {};
+
+ if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
+ if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) {
+ return Some(MaxValidDrawBuffers());
+
+ } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) {
+ return Some(MaxValidDrawBuffers());
+
+ } else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
+ pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + MaxValidDrawBuffers())) {
+ const auto slotId = pname - LOCAL_GL_DRAW_BUFFER0;
+ GLenum ret = LOCAL_GL_NONE;
+ if (!mBoundDrawFramebuffer) {
+ if (slotId == 0) {
+ ret = mDefaultFB_DrawBuffer0;
+ }
+ } else {
+ const auto& fb = *mBoundDrawFramebuffer;
+ const auto& bs = fb.DrawBufferEnabled();
+ if (bs[slotId]) {
+ ret = LOCAL_GL_COLOR_ATTACHMENT0 + slotId;
+ }
+ }
+ return Some(ret);
+ }
+ }
+
+ if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
+ switch (pname) {
+ case LOCAL_GL_TIMESTAMP_EXT: {
+ uint64_t val = 0;
+ if (Has64BitTimestamps()) {
+ gl->fGetInteger64v(pname, (GLint64*)&val);
+ } else {
+ gl->fGetIntegerv(pname, (GLint*)&val);
+ }
+ // TODO: JS doesn't support 64-bit integers. Be lossy and
+ // cast to double (53 bits)
+ return Some(val);
+ }
+
+ case LOCAL_GL_GPU_DISJOINT_EXT: {
+ realGLboolean val = false; // Not disjoint by default.
+ if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
+ gl->fGetBooleanv(pname, &val);
+ }
+ return Some(bool(val));
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (IsWebGL2() ||
+ IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
+ if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) {
+ GLint i = 0;
+ gl->fGetIntegerv(pname, &i);
+ return Some(i);
+ }
+ }
+
+ if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
+ if (pname == LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) {
+ GLfloat f = 0.f;
+ gl->fGetFloatv(pname, &f);
+ return Some(f);
+ }
+ }
+
+ if (IsExtensionEnabled(WebGLExtensionID::MOZ_debug)) {
+ if (pname == dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION) {
+ return Some(mNeedsIndexValidation);
+ }
+ }
+
+ switch (pname) {
+ ////////////////////////////////
+ // Single-value params
+
+ // unsigned int
+ case LOCAL_GL_CULL_FACE_MODE:
+ case LOCAL_GL_FRONT_FACE:
+ case LOCAL_GL_ACTIVE_TEXTURE:
+ case LOCAL_GL_STENCIL_FUNC:
+ case LOCAL_GL_STENCIL_FAIL:
+ case LOCAL_GL_STENCIL_PASS_DEPTH_FAIL:
+ case LOCAL_GL_STENCIL_PASS_DEPTH_PASS:
+ case LOCAL_GL_STENCIL_BACK_FUNC:
+ case LOCAL_GL_STENCIL_BACK_FAIL:
+ case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL:
+ case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS:
+ case LOCAL_GL_DEPTH_FUNC:
+ case LOCAL_GL_BLEND_SRC_RGB:
+ case LOCAL_GL_BLEND_SRC_ALPHA:
+ case LOCAL_GL_BLEND_DST_RGB:
+ case LOCAL_GL_BLEND_DST_ALPHA:
+ case LOCAL_GL_BLEND_EQUATION_RGB:
+ case LOCAL_GL_BLEND_EQUATION_ALPHA: {
+ GLint i = 0;
+ gl->fGetIntegerv(pname, &i);
+ return Some(i);
+ }
+
+ case LOCAL_GL_GENERATE_MIPMAP_HINT:
+ return Some(mGenerateMipmapHint);
+
+ case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT:
+ case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
+ const webgl::FormatUsageInfo* usage;
+ uint32_t width, height;
+ if (!BindCurFBForColorRead(&usage, &width, &height,
+ LOCAL_GL_INVALID_OPERATION))
+ return Nothing();
+
+ const auto implPI = ValidImplementationColorReadPI(usage);
+
+ GLenum ret;
+ if (pname == LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT) {
+ ret = implPI.format;
+ } else {
+ ret = implPI.type;
+ }
+ return Some(ret);
+ }
+
+ // int
+ case LOCAL_GL_STENCIL_REF:
+ case LOCAL_GL_STENCIL_BACK_REF: {
+ GLint stencilBits = 0;
+ if (!GetStencilBits(&stencilBits)) return Nothing();
+
+ // Assuming stencils have 8 bits
+ const GLint stencilMask = (1 << stencilBits) - 1;
+
+ GLint refValue = 0;
+ gl->fGetIntegerv(pname, &refValue);
+
+ return Some(refValue & stencilMask);
+ }
+
+ case LOCAL_GL_SAMPLE_BUFFERS:
+ case LOCAL_GL_SAMPLES: {
+ const auto& fb = mBoundDrawFramebuffer;
+ auto samples = [&]() -> Maybe<uint32_t> {
+ if (!fb) {
+ if (!EnsureDefaultFB()) return Nothing();
+ return Some(mDefaultFB->mSamples);
+ }
+
+ if (!fb->IsCheckFramebufferStatusComplete()) return Some(0);
+
+ DoBindFB(fb, LOCAL_GL_FRAMEBUFFER);
+ return Some(gl->GetIntAs<uint32_t>(LOCAL_GL_SAMPLES));
+ }();
+ if (samples && pname == LOCAL_GL_SAMPLE_BUFFERS) {
+ samples = Some(uint32_t(bool(samples.value())));
+ }
+ if (!samples) return Nothing();
+ return Some(samples.value());
+ }
+
+ case LOCAL_GL_STENCIL_CLEAR_VALUE:
+ case LOCAL_GL_SUBPIXEL_BITS: {
+ GLint i = 0;
+ gl->fGetIntegerv(pname, &i);
+ return Some(i);
+ }
+
+ case LOCAL_GL_RED_BITS:
+ case LOCAL_GL_GREEN_BITS:
+ case LOCAL_GL_BLUE_BITS:
+ case LOCAL_GL_ALPHA_BITS:
+ case LOCAL_GL_DEPTH_BITS:
+ case LOCAL_GL_STENCIL_BITS: {
+ const auto format = [&]() -> const webgl::FormatInfo* {
+ const auto& fb = mBoundDrawFramebuffer;
+ if (fb) {
+ if (!fb->IsCheckFramebufferStatusComplete()) return nullptr;
+
+ const auto& attachment = [&]() -> const auto& {
+ switch (pname) {
+ case LOCAL_GL_DEPTH_BITS:
+ if (fb->DepthStencilAttachment().HasAttachment())
+ return fb->DepthStencilAttachment();
+ return fb->DepthAttachment();
+
+ case LOCAL_GL_STENCIL_BITS:
+ if (fb->DepthStencilAttachment().HasAttachment())
+ return fb->DepthStencilAttachment();
+ return fb->StencilAttachment();
+
+ default:
+ return fb->ColorAttachment0();
+ }
+ }
+ ();
+
+ const auto imageInfo = attachment.GetImageInfo();
+ if (!imageInfo) return nullptr;
+ return imageInfo->mFormat->format;
+ }
+
+ auto effFormat = webgl::EffectiveFormat::RGB8;
+ switch (pname) {
+ case LOCAL_GL_DEPTH_BITS:
+ if (mOptions.depth) {
+ effFormat = webgl::EffectiveFormat::DEPTH24_STENCIL8;
+ }
+ break;
+
+ case LOCAL_GL_STENCIL_BITS:
+ if (mOptions.stencil) {
+ effFormat = webgl::EffectiveFormat::DEPTH24_STENCIL8;
+ }
+ break;
+
+ default:
+ if (mOptions.alpha) {
+ effFormat = webgl::EffectiveFormat::RGBA8;
+ }
+ break;
+ }
+ return webgl::GetFormat(effFormat);
+ }();
+ int32_t ret = 0;
+ if (format) {
+ switch (pname) {
+ case LOCAL_GL_RED_BITS:
+ ret = format->r;
+ break;
+ case LOCAL_GL_GREEN_BITS:
+ ret = format->g;
+ break;
+ case LOCAL_GL_BLUE_BITS:
+ ret = format->b;
+ break;
+ case LOCAL_GL_ALPHA_BITS:
+ ret = format->a;
+ break;
+ case LOCAL_GL_DEPTH_BITS:
+ ret = format->d;
+ break;
+ case LOCAL_GL_STENCIL_BITS:
+ ret = format->s;
+ break;
+ }
+ }
+ return Some(ret);
+ }
+
+ case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
+ return Some(mGLMaxRenderbufferSize);
+
+ case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
+ return Some(mGLMaxVertexTextureImageUnits);
+
+ case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
+ return Some(mGLMaxFragmentTextureImageUnits);
+
+ case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS:
+ return Some(mGLMaxVertexUniformVectors);
+
+ case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
+ return Some(mGLMaxFragmentUniformVectors);
+
+ case LOCAL_GL_MAX_VARYING_VECTORS:
+ return Some(mGLMaxFragmentInputVectors);
+
+ // unsigned int. here we may have to return very large values like 2^32-1
+ // that can't be represented as javascript integer values. We just return
+ // them as doubles and javascript doesn't care.
+ case LOCAL_GL_STENCIL_BACK_VALUE_MASK:
+ return Some(mStencilValueMaskBack);
+ // pass as FP value to allow large values such as 2^32-1.
+
+ case LOCAL_GL_STENCIL_BACK_WRITEMASK:
+ return Some(mStencilWriteMaskBack);
+
+ case LOCAL_GL_STENCIL_VALUE_MASK:
+ return Some(mStencilValueMaskFront);
+
+ case LOCAL_GL_STENCIL_WRITEMASK:
+ return Some(mStencilWriteMaskFront);
+
+ case LOCAL_GL_COLOR_WRITEMASK:
+ return Some(mColorWriteMask0);
+
+ // float
+ case LOCAL_GL_LINE_WIDTH:
+ return Some((double)mLineWidth);
+
+ case LOCAL_GL_DEPTH_CLEAR_VALUE:
+ case LOCAL_GL_POLYGON_OFFSET_FACTOR:
+ case LOCAL_GL_POLYGON_OFFSET_UNITS:
+ case LOCAL_GL_SAMPLE_COVERAGE_VALUE: {
+ GLfloat f = 0.f;
+ gl->fGetFloatv(pname, &f);
+ return Some(f);
+ }
+
+ // bool
+ case LOCAL_GL_DEPTH_TEST:
+ return Some((bool)mDepthTestEnabled);
+ case LOCAL_GL_STENCIL_TEST:
+ return Some((bool)mStencilTestEnabled);
+
+ case LOCAL_GL_BLEND:
+ case LOCAL_GL_CULL_FACE:
+ case LOCAL_GL_DITHER:
+ case LOCAL_GL_POLYGON_OFFSET_FILL:
+ case LOCAL_GL_SCISSOR_TEST:
+ case LOCAL_GL_SAMPLE_COVERAGE_INVERT:
+ case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
+ case LOCAL_GL_SAMPLE_COVERAGE:
+ case LOCAL_GL_DEPTH_WRITEMASK: {
+ realGLboolean b = 0;
+ gl->fGetBooleanv(pname, &b);
+ return Some(bool(b));
+ }
+
+ default:
+ break;
+ }
+
+ ErrorInvalidEnumInfo("pname", pname);
+ return Nothing();
+}
+
+bool WebGLContext::IsEnabled(GLenum cap) {
+ const FuncScope funcScope(*this, "isEnabled");
+ if (IsContextLost()) return false;
+
+ if (!ValidateCapabilityEnum(cap)) return false;
+
+ const auto& slot = GetStateTrackingSlot(cap, 0);
+ if (slot) return *slot;
+
+ return gl->fIsEnabled(cap);
+}
+
+bool WebGLContext::ValidateCapabilityEnum(GLenum cap) {
+ switch (cap) {
+ case LOCAL_GL_BLEND:
+ case LOCAL_GL_CULL_FACE:
+ case LOCAL_GL_DEPTH_TEST:
+ case LOCAL_GL_DITHER:
+ case LOCAL_GL_POLYGON_OFFSET_FILL:
+ case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
+ case LOCAL_GL_SAMPLE_COVERAGE:
+ case LOCAL_GL_SCISSOR_TEST:
+ case LOCAL_GL_STENCIL_TEST:
+ return true;
+ case LOCAL_GL_RASTERIZER_DISCARD:
+ return IsWebGL2();
+ default:
+ ErrorInvalidEnumInfo("cap", cap);
+ return false;
+ }
+}
+
+bool* WebGLContext::GetStateTrackingSlot(GLenum cap, GLuint i) {
+ switch (cap) {
+ case LOCAL_GL_DEPTH_TEST:
+ return &mDepthTestEnabled;
+ case LOCAL_GL_DITHER:
+ return &mDitherEnabled;
+ case LOCAL_GL_RASTERIZER_DISCARD:
+ return &mRasterizerDiscardEnabled;
+ case LOCAL_GL_SCISSOR_TEST:
+ return &mScissorTestEnabled;
+ case LOCAL_GL_STENCIL_TEST:
+ return &mStencilTestEnabled;
+ }
+
+ return nullptr;
+}
+
+} // namespace mozilla