diff options
Diffstat (limited to 'dom/canvas/WebGLContextState.cpp')
-rw-r--r-- | dom/canvas/WebGLContextState.cpp | 484 |
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 |