mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
fa284e5717
Differential Revision: https://phabricator.services.mozilla.com/D122916
485 lines
13 KiB
C++
485 lines
13 KiB
C++
/* -*- 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
|