webgl::ShaderValidator* WebGLContext::CreateShaderValidator(GLenum shaderType) const { if (mBypassShaderValidation) return nullptr; const auto spec = (IsWebGL2() ? SH_WEBGL2_SPEC : SH_WEBGL_SPEC); const auto outputLanguage = ShaderOutput(gl); ShBuiltInResources resources; memset(&resources, 0, sizeof(resources)); ShInitBuiltInResources(&resources); resources.HashFunction = webgl::IdentifierHashFunc; resources.MaxVertexAttribs = mGLMaxVertexAttribs; resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors; resources.MaxVaryingVectors = mGLMaxVaryingVectors; resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits; resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits; resources.MaxTextureImageUnits = mGLMaxTextureImageUnits; resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors; const bool hasMRTs = (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)); resources.MaxDrawBuffers = (hasMRTs ? mGLMaxDrawBuffers : 1); if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth)) resources.EXT_frag_depth = 1; if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) resources.OES_standard_derivatives = 1; if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) resources.EXT_draw_buffers = 1; if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod)) resources.EXT_shader_texture_lod = 1; // Tell ANGLE to allow highp in frag shaders. (unless disabled) // If underlying GLES doesn't have highp in frag shaders, it should complain anyways. resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1; if (gl->WorkAroundDriverBugs()) { #ifdef XP_MACOSX if (gl->Vendor() == gl::GLVendor::NVIDIA) { // Work around bug 890432 resources.MaxExpressionComplexity = 1000; } #endif } const auto compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl); return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources, compileOptions); }
void WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex) { const FuncScope funcScope(*this, "bindTexture"); if (IsContextLost()) return; if (newTex && !ValidateObject("tex", *newTex)) return; // Need to check rawTarget first before comparing against newTex->Target() as // newTex->Target() returns a TexTarget, which will assert on invalid value. WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr; switch (rawTarget) { case LOCAL_GL_TEXTURE_2D: currentTexPtr = &mBound2DTextures[mActiveTexture]; break; case LOCAL_GL_TEXTURE_CUBE_MAP: currentTexPtr = &mBoundCubeMapTextures[mActiveTexture]; break; case LOCAL_GL_TEXTURE_3D: if (IsWebGL2()) currentTexPtr = &mBound3DTextures[mActiveTexture]; break; case LOCAL_GL_TEXTURE_2D_ARRAY: if (IsWebGL2()) currentTexPtr = &mBound2DArrayTextures[mActiveTexture]; break; } if (!currentTexPtr) { ErrorInvalidEnumInfo("target", rawTarget); return; } const TexTarget texTarget(rawTarget); if (newTex) { if (!newTex->BindTexture(texTarget)) return; } else { gl->fBindTexture(texTarget.get(), 0); } *currentTexPtr = newTex; }
bool WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info) { switch (mode) { case LOCAL_GL_FUNC_ADD: case LOCAL_GL_FUNC_SUBTRACT: case LOCAL_GL_FUNC_REVERSE_SUBTRACT: return true; case LOCAL_GL_MIN: case LOCAL_GL_MAX: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax)) { return true; } break; default: break; } ErrorInvalidEnumInfo(info, mode); return false; }
bool WebGLContext::ValidateFramebufferTarget(GLenum target, const char* const info) { bool isValid = true; switch (target) { case LOCAL_GL_FRAMEBUFFER: break; case LOCAL_GL_DRAW_FRAMEBUFFER: case LOCAL_GL_READ_FRAMEBUFFER: isValid = IsWebGL2(); break; default: isValid = false; break; } if (MOZ_LIKELY(isValid)) { return true; } ErrorInvalidEnum("%s: Invalid target: %s (0x%04x).", info, EnumName(target), target); return false; }
WebGLRefPtr<WebGLQuery>* WebGLContext::ValidateQuerySlotByTarget( GLenum target) { if (IsWebGL2()) { switch (target) { case LOCAL_GL_ANY_SAMPLES_PASSED: case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: return &mQuerySlot_SamplesPassed; case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: return &mQuerySlot_TFPrimsWritten; default: break; } } if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) { switch (target) { case LOCAL_GL_TIME_ELAPSED_EXT: return &mQuerySlot_TimeElapsed; default: break; } } ErrorInvalidEnumInfo("target", target); return nullptr; }
bool WebGLContext::InitWebGL2() { MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!"); // check OpenGL features if (!gl->IsSupported(gl::GLFeature::occlusion_query) && !gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) { // On desktop, we fake occlusion_query_boolean with occlusion_query if // necessary. (See WebGL2ContextQueries.cpp) GenerateWarning("WebGL 2 unavailable. Requires occlusion queries."); return false; } std::vector<gl::GLFeature> missingList; for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) { if (!gl->IsSupported(kRequiredFeatures[i])) missingList.push_back(kRequiredFeatures[i]); } if (missingList.size()) { nsAutoCString exts; for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) { exts.AppendLiteral("\n "); exts.Append(gl::GLContext::GetFeatureName(*itr)); } GenerateWarning("WebGL 2 unavailable. The following required features are" " unavailible: %s", exts.BeginReading()); return false; } // ok WebGL 2 is compatible, we can enable natively supported extensions. for (size_t i = 0; i < ArrayLength(kNativelySupportedExtensions); i++) { EnableExtension(kNativelySupportedExtensions[i]); MOZ_ASSERT(IsExtensionEnabled(kNativelySupportedExtensions[i])); } // we initialise WebGL 2 related stuff. gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &mGLMaxTransformFeedbackSeparateAttribs); gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS, &mGLMaxUniformBufferBindings); mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs); mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings); mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; mBypassShaderValidation = true; return true; }
void WebGLContext::DeleteBuffer(WebGLBuffer* buffer) { if (IsContextLost()) return; if (!ValidateObjectAllowDeletedOrNull("deleteBuffer", buffer)) return; if (!buffer || buffer->IsDeleted()) return; //// const auto fnClearIfBuffer = [&](WebGLRefPtr<WebGLBuffer>& bindPoint) { if (bindPoint == buffer) { bindPoint = nullptr; } }; fnClearIfBuffer(mBoundArrayBuffer); fnClearIfBuffer(mBoundVertexArray->mElementArrayBuffer); // WebGL binding points if (IsWebGL2()) { fnClearIfBuffer(mBoundCopyReadBuffer); fnClearIfBuffer(mBoundCopyWriteBuffer); fnClearIfBuffer(mBoundPixelPackBuffer); fnClearIfBuffer(mBoundPixelUnpackBuffer); fnClearIfBuffer(mBoundUniformBuffer); fnClearIfBuffer(mBoundTransformFeedback->mGenericBufferBinding); if (!mBoundTransformFeedback->mIsActive) { for (auto& binding : mBoundTransformFeedback->mIndexedBindings) { fnClearIfBuffer(binding.mBufferBinding); } } for (auto& binding : mIndexedUniformBufferBindings) { fnClearIfBuffer(binding.mBufferBinding); } } for (int32_t i = 0; i < mGLMaxVertexAttribs; i++) { if (mBoundVertexArray->HasAttrib(i)) { fnClearIfBuffer(mBoundVertexArray->mAttribs[i].mBuf); } } //// buffer->RequestDelete(); }
void WebGLContext::AssertCachedBindings() const { #ifdef DEBUG gl::GLContext::LocalErrorScope errorScope(*gl); if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, mBoundVertexArray->mGLName); } GLint stencilBits = 0; if (GetStencilBits(&stencilBits)) { // Depends on current draw framebuffer. const GLuint stencilRefMask = (1 << stencilBits) - 1; AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF, stencilRefMask, mStencilRefFront); AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack); } // Program GLuint bound = mCurrentProgram ? mCurrentProgram->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound); // Textures GLenum activeTexture = mActiveTexture + LOCAL_GL_TEXTURE0; AssertUintParamCorrect(gl, LOCAL_GL_ACTIVE_TEXTURE, activeTexture); WebGLTexture* curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_2D); bound = curTex ? curTex->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_2D, bound); curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_CUBE_MAP); bound = curTex ? curTex->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_CUBE_MAP, bound); // Buffers bound = mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_ARRAY_BUFFER_BINDING, bound); MOZ_ASSERT(mBoundVertexArray); WebGLBuffer* curBuff = mBoundVertexArray->mElementArrayBuffer; bound = curBuff ? curBuff->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING, bound); MOZ_ASSERT(!gl::GLContext::IsBadCallError(errorScope.GetError())); #endif // We do not check the renderbuffer binding, because we never rely on it // matching. }
WebGLRefPtr<WebGLBuffer>* WebGLContext::ValidateBufferSlot(const char* funcName, GLenum target) { WebGLRefPtr<WebGLBuffer>* slot = nullptr; switch (target) { case LOCAL_GL_ARRAY_BUFFER: slot = &mBoundArrayBuffer; break; case LOCAL_GL_ELEMENT_ARRAY_BUFFER: slot = &(mBoundVertexArray->mElementArrayBuffer); break; } if (IsWebGL2()) { switch (target) { case LOCAL_GL_COPY_READ_BUFFER: slot = &mBoundCopyReadBuffer; break; case LOCAL_GL_COPY_WRITE_BUFFER: slot = &mBoundCopyWriteBuffer; break; case LOCAL_GL_PIXEL_PACK_BUFFER: slot = &mBoundPixelPackBuffer; break; case LOCAL_GL_PIXEL_UNPACK_BUFFER: slot = &mBoundPixelUnpackBuffer; break; case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: slot = &(mBoundTransformFeedback->mGenericBufferBinding); break; case LOCAL_GL_UNIFORM_BUFFER: slot = &mBoundUniformBuffer; break; } } if (!slot) { ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target); return nullptr; } return slot; }
bool WebGLContext::DrawArrays_check(const char* funcName, GLenum mode, GLint first, GLsizei vertCount, GLsizei instanceCount) { if (!ValidateDrawModeEnum(mode, funcName)) return false; if (!ValidateNonNegative(funcName, "first", first) || !ValidateNonNegative(funcName, "vertCount", vertCount) || !ValidateNonNegative(funcName, "instanceCount", instanceCount)) { return false; } if (!ValidateStencilParamsForDrawCall()) return false; if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); if (mPrimRestartTypeBytes != 0) { mPrimRestartTypeBytes = 0; // OSX appears to have severe perf issues with leaving this enabled. gl->fDisable(LOCAL_GL_PRIMITIVE_RESTART); } } if (!vertCount || !instanceCount) return false; // No error, just early out. if (!ValidateBufferFetching(funcName)) return false; const auto checked_firstPlusCount = CheckedInt<GLsizei>(first) + vertCount; if (!checked_firstPlusCount.isValid()) { ErrorInvalidOperation("%s: overflow in first+vertCount", funcName); return false; } if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) { ErrorInvalidOperation("%s: Bound vertex attribute buffers do not have sufficient" " size for given first and count.", funcName); return false; } return true; }
void WebGLContext::DeleteBuffer(WebGLBuffer* buffer) { const FuncScope funcScope(*this, "deleteBuffer"); if (!ValidateDeleteObject(buffer)) return; //// const auto fnClearIfBuffer = [&](GLenum target, WebGLRefPtr<WebGLBuffer>& bindPoint) { if (bindPoint == buffer) { WebGLBuffer::SetSlot(target, nullptr, &bindPoint); } }; fnClearIfBuffer(0, mBoundArrayBuffer); fnClearIfBuffer(0, mBoundVertexArray->mElementArrayBuffer); for (auto& cur : mBoundVertexArray->mAttribs) { fnClearIfBuffer(0, cur.mBuf); } // WebGL binding points if (IsWebGL2()) { fnClearIfBuffer(0, mBoundCopyReadBuffer); fnClearIfBuffer(0, mBoundCopyWriteBuffer); fnClearIfBuffer(0, mBoundPixelPackBuffer); fnClearIfBuffer(0, mBoundPixelUnpackBuffer); fnClearIfBuffer(0, mBoundUniformBuffer); fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, mBoundTransformFeedbackBuffer); if (!mBoundTransformFeedback->mIsActive) { for (auto& binding : mBoundTransformFeedback->mIndexedBindings) { fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, binding.mBufferBinding); } } for (auto& binding : mIndexedUniformBufferBindings) { fnClearIfBuffer(0, binding.mBufferBinding); } } //// buffer->RequestDelete(); }
void WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex) { if (IsContextLost()) return; if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex)) return; // Need to check rawTarget first before comparing against newTex->Target() as // newTex->Target() returns a TexTarget, which will assert on invalid value. WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr; switch (rawTarget) { case LOCAL_GL_TEXTURE_2D: currentTexPtr = &mBound2DTextures[mActiveTexture]; break; case LOCAL_GL_TEXTURE_CUBE_MAP: currentTexPtr = &mBoundCubeMapTextures[mActiveTexture]; break; case LOCAL_GL_TEXTURE_3D: if (!IsWebGL2()) return ErrorInvalidEnum("bindTexture: target TEXTURE_3D is only available in WebGL version 2.0 or newer"); currentTexPtr = &mBound3DTextures[mActiveTexture]; break; default: return ErrorInvalidEnumInfo("bindTexture: target", rawTarget); } const TexTarget texTarget(rawTarget); MakeContextCurrent(); if (newTex && !newTex->BindTexture(texTarget)) return; if (!newTex) { gl->fBindTexture(texTarget.get(), 0); } *currentTexPtr = newTex; }
bool WebGLContext::DrawInstanced_check(const char* info) { MOZ_ASSERT(IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)); if (!mBufferFetchingHasPerVertex) { /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt * If all of the enabled vertex attribute arrays that are bound to active * generic attributes in the program have a non-zero divisor, the draw * call should return INVALID_OPERATION. * * NB: This also appears to apply to NV_instanced_arrays, though the * INVALID_OPERATION emission is not explicitly stated. * ARB_instanced_arrays does not have this restriction. */ ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info); return false; } return true; }
bool WebGLContext::ValidateCapabilityEnum(GLenum cap, const char* info) { 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(info, cap); return false; } }
bool WebGLContext::ValidateTextureTargetEnum(GLenum target, const char* info) { switch (target) { case LOCAL_GL_TEXTURE_2D: case LOCAL_GL_TEXTURE_CUBE_MAP: return true; case LOCAL_GL_TEXTURE_3D: if (IsWebGL2()) return true; break; default: break; } ErrorInvalidEnumInfo(info, target); return false; }
void WebGLContext::DeleteBuffer(WebGLBuffer* buffer) { if (IsContextLost()) return; if (!ValidateObjectAllowDeletedOrNull("deleteBuffer", buffer)) return; if (!buffer || buffer->IsDeleted()) return; // TODO: Extract this into a helper function? if (mBoundArrayBuffer == buffer) { WebGLContextUnchecked::BindBuffer(LOCAL_GL_ARRAY_BUFFER, nullptr); mBoundArrayBuffer = nullptr; } if (mBoundVertexArray->mElementArrayBuffer == buffer) { WebGLContextUnchecked::BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, nullptr); mBoundVertexArray->mElementArrayBuffer = nullptr; } // WebGL binding points if (IsWebGL2()) { if (mBoundCopyReadBuffer == buffer) mBoundCopyReadBuffer = nullptr; if (mBoundCopyWriteBuffer == buffer) mBoundCopyWriteBuffer = nullptr; if (mBoundPixelPackBuffer == buffer) mBoundPixelPackBuffer = nullptr; if (mBoundPixelUnpackBuffer == buffer) mBoundPixelUnpackBuffer = nullptr; if (mBoundTransformFeedbackBuffer == buffer) mBoundTransformFeedbackBuffer = nullptr; if (mBoundUniformBuffer == buffer) mBoundUniformBuffer = nullptr; const size_t xfBufferCount = mBoundTransformFeedbackBuffers.Length(); for (size_t n = 0; n < xfBufferCount; n++) { if (mBoundTransformFeedbackBuffers[n] == buffer) { mBoundTransformFeedbackBuffers[n] = nullptr; } } const size_t uniformBufferCount = mBoundUniformBuffers.Length(); for (size_t n = 0; n < uniformBufferCount; n++) { if (mBoundUniformBuffers[n] == buffer) { mBoundUniformBuffers[n] = nullptr; } } } for (int32_t i = 0; i < mGLMaxVertexAttribs; i++) { if (mBoundVertexArray->HasAttrib(i) && mBoundVertexArray->mAttribs[i].buf == buffer) { mBoundVertexArray->mAttribs[i].buf = nullptr; } } buffer->RequestDelete(); }
void WebGLContext::AssertCachedGlobalState() const { #ifdef DEBUG gl::GLContext::LocalErrorScope errorScope(*gl); //////////////// // Draw state MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DITHER) == mDitherEnabled); MOZ_ASSERT_IF(IsWebGL2(), gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled); MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled); // Cannot trivially check COLOR_CLEAR_VALUE, since in old GL versions glGet // may clamp based on whether the current framebuffer is floating-point or // not. This also means COLOR_CLEAR_VALUE save+restore is dangerous! realGLboolean depthWriteMask = 0; gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask); MOZ_ASSERT(depthWriteMask == mDepthWriteMask); GLfloat depthClearValue = 0.0f; gl->fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue); MOZ_ASSERT(IsCacheCorrect(mDepthClearValue, depthClearValue)); const int maxStencilBits = 8; const GLuint maxStencilBitsMask = (1 << maxStencilBits) - 1; AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_CLEAR_VALUE, maxStencilBitsMask, mStencilClearValue); // GLES 3.0.4, $4.1.4, p177: // [...] the front and back stencil mask are both set to the value `2^s - // 1`, where `s` is greater than or equal to the number of bits in the // deepest stencil buffer supported by the GL implementation. AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK, maxStencilBitsMask, mStencilValueMaskFront); AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, maxStencilBitsMask, mStencilValueMaskBack); AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK, maxStencilBitsMask, mStencilWriteMaskFront); AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK, maxStencilBitsMask, mStencilWriteMaskBack); // Viewport GLint int4[4] = {0, 0, 0, 0}; gl->fGetIntegerv(LOCAL_GL_VIEWPORT, int4); MOZ_ASSERT(int4[0] == mViewportX && int4[1] == mViewportY && int4[2] == mViewportWidth && int4[3] == mViewportHeight); AssertUintParamCorrect(gl, LOCAL_GL_PACK_ALIGNMENT, mPixelStore_PackAlignment); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ALIGNMENT, mPixelStore_UnpackAlignment); if (IsWebGL2()) { AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_IMAGE_HEIGHT, mPixelStore_UnpackImageHeight); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_IMAGES, mPixelStore_UnpackSkipImages); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ROW_LENGTH, mPixelStore_UnpackRowLength); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_ROWS, mPixelStore_UnpackSkipRows); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_PIXELS, mPixelStore_UnpackSkipPixels); AssertUintParamCorrect(gl, LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength); AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows); AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels); } MOZ_ASSERT(!gl::GLContext::IsBadCallError(errorScope.GetError())); #endif }
JS::Value WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) { if (IsContextLost()) return JS::NullValue(); MakeContextCurrent(); if (MinCapabilityMode()) { switch(pname) { //////////////////////////// // Single-value params // int case LOCAL_GL_MAX_VERTEX_ATTRIBS: return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_ATTRIBS); case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS: return JS::Int32Value(MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS); case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS: return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS); case LOCAL_GL_MAX_VARYING_VECTORS: return JS::Int32Value(MINVALUE_GL_MAX_VARYING_VECTORS); case LOCAL_GL_MAX_TEXTURE_SIZE: return JS::Int32Value(MINVALUE_GL_MAX_TEXTURE_SIZE); case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE: return JS::Int32Value(MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE); case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: return JS::Int32Value(MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS); case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS); case LOCAL_GL_MAX_RENDERBUFFER_SIZE: return JS::Int32Value(MINVALUE_GL_MAX_RENDERBUFFER_SIZE); default: // Return the real value; we're not overriding this one break; } } if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) { return JS::Int32Value(mImplMaxColorAttachments); } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) { return JS::Int32Value(mImplMaxDrawBuffers); } else if (pname >= LOCAL_GL_DRAW_BUFFER0 && pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mImplMaxDrawBuffers)) { GLint iv = 0; gl->fGetIntegerv(pname, &iv); if (mBoundDrawFramebuffer) return JS::Int32Value(iv); const GLint index = (pname - LOCAL_GL_DRAW_BUFFER0); if (iv == LOCAL_GL_COLOR_ATTACHMENT0 + index) return JS::Int32Value(LOCAL_GL_BACK); return JS::Int32Value(LOCAL_GL_NONE); } } if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) { WebGLVertexArray* vao = (mBoundVertexArray != mDefaultVertexArray) ? mBoundVertexArray.get() : nullptr; return WebGLObjectAsJSValue(cx, vao, rv); } } if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) { if (pname == LOCAL_GL_TIMESTAMP_EXT) { GLuint64 iv = 0; gl->fGetInteger64v(pname, (GLint64*) &iv); // TODO: JS doesn't support 64-bit integers. Be lossy and // cast to double (53 bits) return JS::NumberValue(static_cast<double>(iv)); } else if (pname == LOCAL_GL_GPU_DISJOINT_EXT) { // When disjoint isn't supported, leave as false. realGLboolean disjoint = LOCAL_GL_FALSE; if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) { gl->fGetBooleanv(pname, &disjoint); } return JS::BooleanValue(bool(disjoint)); } } // Privileged string params exposed by WEBGL_debug_renderer_info. // The privilege check is done in WebGLContext::IsExtensionSupported. // So here we just have to check that the extension is enabled. if (IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info)) { switch (pname) { case UNMASKED_VENDOR_WEBGL: case UNMASKED_RENDERER_WEBGL: { const char* overridePref = nullptr; GLenum driverEnum = LOCAL_GL_NONE; switch (pname) { case UNMASKED_RENDERER_WEBGL: overridePref = "webgl.renderer-string-override"; driverEnum = LOCAL_GL_RENDERER; break; case UNMASKED_VENDOR_WEBGL: overridePref = "webgl.vendor-string-override"; driverEnum = LOCAL_GL_VENDOR; break; default: MOZ_CRASH("bad `pname`"); } bool hasRetVal = false; nsAutoString ret; if (overridePref) { nsresult res = Preferences::GetString(overridePref, &ret); if (NS_SUCCEEDED(res) && ret.Length() > 0) hasRetVal = true; } if (!hasRetVal) { const char* chars = reinterpret_cast<const char*>(gl->fGetString(driverEnum)); ret = NS_ConvertASCIItoUTF16(chars); hasRetVal = true; } return StringValue(cx, ret, rv); } } } if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) { if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::Int32Value(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 JS::NumberValue(f); } } switch (pname) { // // String params // case LOCAL_GL_VENDOR: case LOCAL_GL_RENDERER: return StringValue(cx, "Mozilla", rv); case LOCAL_GL_VERSION: return StringValue(cx, "WebGL 1.0", rv); case LOCAL_GL_SHADING_LANGUAGE_VERSION: return StringValue(cx, "WebGL GLSL ES 1.0", rv); //////////////////////////////// // 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: case LOCAL_GL_GENERATE_MIPMAP_HINT: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::NumberValue(uint32_t(i)); } case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: { if (mBoundReadFramebuffer) { nsCString fbStatusInfoIgnored; const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(&fbStatusInfoIgnored); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { ErrorInvalidOperation("getParameter: Read framebuffer must be" " complete before querying" " IMPLEMENTATION_COLOR_READ_TYPE."); return JS::NullValue(); } } GLint i = 0; if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { gl->fGetIntegerv(pname, &i); } else { i = LOCAL_GL_UNSIGNED_BYTE; } return JS::NumberValue(uint32_t(i)); } case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: { if (mBoundReadFramebuffer) { nsCString fbStatusInfoIgnored; const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(&fbStatusInfoIgnored); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { ErrorInvalidOperation("getParameter: Read framebuffer must be" " complete before querying" " IMPLEMENTATION_COLOR_READ_FORMAT."); return JS::NullValue(); } } GLint i = 0; if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { gl->fGetIntegerv(pname, &i); } else { i = LOCAL_GL_RGBA; } // OpenGL ES 3.0.4 p112 Table 3.2 shows that read format SRGB_ALPHA is // not supported. And if internal format of fbo is SRGB8_ALPHA8, then // IMPLEMENTATION_COLOR_READ_FORMAT is SRGB_ALPHA which is not supported // by ReadPixels. So, just return RGBA here. if (i == LOCAL_GL_SRGB_ALPHA) i = LOCAL_GL_RGBA; return JS::NumberValue(uint32_t(i)); } // int case LOCAL_GL_STENCIL_REF: case LOCAL_GL_STENCIL_BACK_REF: { GLint stencilBits = 0; if (!GetStencilBits(&stencilBits)) return JS::NullValue(); // Assuming stencils have 8 bits const GLint stencilMask = (1 << stencilBits) - 1; GLint refValue = 0; gl->fGetIntegerv(pname, &refValue); return JS::Int32Value(refValue & stencilMask); } case LOCAL_GL_STENCIL_BITS: { GLint stencilBits = 0; GetStencilBits(&stencilBits); return JS::Int32Value(stencilBits); } case LOCAL_GL_STENCIL_CLEAR_VALUE: case LOCAL_GL_UNPACK_ALIGNMENT: case LOCAL_GL_PACK_ALIGNMENT: case LOCAL_GL_SUBPIXEL_BITS: case LOCAL_GL_SAMPLE_BUFFERS: case LOCAL_GL_SAMPLES: case LOCAL_GL_MAX_VERTEX_ATTRIBS: case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: case LOCAL_GL_RED_BITS: case LOCAL_GL_GREEN_BITS: case LOCAL_GL_BLUE_BITS: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::Int32Value(i); } case LOCAL_GL_DEPTH_BITS: { GLint i = 0; if (!mNeedsFakeNoDepth) { gl->fGetIntegerv(pname, &i); } return JS::Int32Value(i); } case LOCAL_GL_ALPHA_BITS: { GLint i = 0; if (!mNeedsFakeNoAlpha) { gl->fGetIntegerv(pname, &i); } return JS::Int32Value(i); } case LOCAL_GL_MAX_TEXTURE_SIZE: return JS::Int32Value(mImplMaxTextureSize); case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE: return JS::Int32Value(mImplMaxCubeMapTextureSize); case LOCAL_GL_MAX_RENDERBUFFER_SIZE: return JS::Int32Value(mImplMaxRenderbufferSize); case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS: return JS::Int32Value(mGLMaxVertexUniformVectors); case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS: return JS::Int32Value(mGLMaxFragmentUniformVectors); case LOCAL_GL_MAX_VARYING_VECTORS: return JS::Int32Value(mGLMaxVaryingVectors); case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: { uint32_t length = mCompressedTextureFormats.Length(); JSObject* obj = dom::Uint32Array::Create(cx, this, length, mCompressedTextureFormats.Elements()); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 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 JS::DoubleValue(mStencilValueMaskBack); // pass as FP value to allow large values such as 2^32-1. case LOCAL_GL_STENCIL_BACK_WRITEMASK: return JS::DoubleValue(mStencilWriteMaskBack); case LOCAL_GL_STENCIL_VALUE_MASK: return JS::DoubleValue(mStencilValueMaskFront); case LOCAL_GL_STENCIL_WRITEMASK: return JS::DoubleValue(mStencilWriteMaskFront); // float case LOCAL_GL_DEPTH_CLEAR_VALUE: case LOCAL_GL_LINE_WIDTH: 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 JS::DoubleValue(f); } // bool case LOCAL_GL_BLEND: case LOCAL_GL_DEPTH_TEST: case LOCAL_GL_STENCIL_TEST: 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_DEPTH_WRITEMASK: { realGLboolean b = 0; gl->fGetBooleanv(pname, &b); return JS::BooleanValue(bool(b)); } // bool, WebGL-specific case UNPACK_FLIP_Y_WEBGL: return JS::BooleanValue(mPixelStore_FlipY); case UNPACK_PREMULTIPLY_ALPHA_WEBGL: return JS::BooleanValue(mPixelStore_PremultiplyAlpha); // uint, WebGL-specific case UNPACK_COLORSPACE_CONVERSION_WEBGL: return JS::NumberValue(uint32_t(mPixelStore_ColorspaceConversion)); //////////////////////////////// // Complex values // 2 floats case LOCAL_GL_DEPTH_RANGE: case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: { GLfloat fv[2] = { 0 }; gl->fGetFloatv(pname, fv); JSObject* obj = dom::Float32Array::Create(cx, this, 2, fv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 4 floats case LOCAL_GL_COLOR_CLEAR_VALUE: case LOCAL_GL_BLEND_COLOR: { GLfloat fv[4] = { 0 }; gl->fGetFloatv(pname, fv); JSObject* obj = dom::Float32Array::Create(cx, this, 4, fv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 2 ints case LOCAL_GL_MAX_VIEWPORT_DIMS: { GLint iv[2] = { 0 }; gl->fGetIntegerv(pname, iv); JSObject* obj = dom::Int32Array::Create(cx, this, 2, iv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 4 ints case LOCAL_GL_SCISSOR_BOX: case LOCAL_GL_VIEWPORT: { GLint iv[4] = { 0 }; gl->fGetIntegerv(pname, iv); JSObject* obj = dom::Int32Array::Create(cx, this, 4, iv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 4 bools case LOCAL_GL_COLOR_WRITEMASK: { realGLboolean gl_bv[4] = { 0 }; gl->fGetBooleanv(pname, gl_bv); bool vals[4] = { bool(gl_bv[0]), bool(gl_bv[1]), bool(gl_bv[2]), bool(gl_bv[3]) }; JS::Rooted<JS::Value> arr(cx); if (!dom::ToJSValue(cx, vals, &arr)) { rv = NS_ERROR_OUT_OF_MEMORY; } return arr; } case LOCAL_GL_ARRAY_BUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundArrayBuffer.get(), rv); } case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundVertexArray->mElementArrayBuffer.get(), rv); } case LOCAL_GL_RENDERBUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundRenderbuffer.get(), rv); } // DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING. case LOCAL_GL_FRAMEBUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundDrawFramebuffer.get(), rv); } case LOCAL_GL_CURRENT_PROGRAM: { return WebGLObjectAsJSValue(cx, mCurrentProgram.get(), rv); } case LOCAL_GL_TEXTURE_BINDING_2D: { return WebGLObjectAsJSValue(cx, mBound2DTextures[mActiveTexture].get(), rv); } case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: { return WebGLObjectAsJSValue(cx, mBoundCubeMapTextures[mActiveTexture].get(), rv); } default: break; } ErrorInvalidEnumInfo("getParameter: parameter", pname); return JS::NullValue(); }
void WebGLContext::VertexAttribAnyPointer(const char* funcName, bool isFuncInt, GLuint index, GLint size, GLenum type, bool normalized, GLsizei stride, WebGLintptr byteOffset) { if (IsContextLost()) return; if (!ValidateAttribIndex(index, funcName)) return; //// if (size < 1 || size > 4) { ErrorInvalidValue("%s: invalid element size", funcName); return; } // see WebGL spec section 6.6 "Vertex Attribute Data Stride" if (stride < 0 || stride > 255) { ErrorInvalidValue("%s: negative or too large stride", funcName); return; } if (byteOffset < 0) { ErrorInvalidValue("%s: negative offset", funcName); return; } //// bool isTypeValid = true; uint8_t typeAlignment; switch (type) { // WebGL 1: case LOCAL_GL_BYTE: case LOCAL_GL_UNSIGNED_BYTE: typeAlignment = 1; break; case LOCAL_GL_SHORT: case LOCAL_GL_UNSIGNED_SHORT: typeAlignment = 2; break; case LOCAL_GL_FLOAT: if (isFuncInt) { isTypeValid = false; } typeAlignment = 4; break; // WebGL 2: case LOCAL_GL_INT: case LOCAL_GL_UNSIGNED_INT: if (!IsWebGL2()) { isTypeValid = false; } typeAlignment = 4; break; case LOCAL_GL_HALF_FLOAT: if (isFuncInt || !IsWebGL2()) { isTypeValid = false; } typeAlignment = 2; break; case LOCAL_GL_FIXED: if (isFuncInt || !IsWebGL2()) { isTypeValid = false; } typeAlignment = 4; break; case LOCAL_GL_INT_2_10_10_10_REV: case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: if (isFuncInt || !IsWebGL2()) { isTypeValid = false; break; } if (size != 4) { ErrorInvalidOperation("%s: size must be 4 for this type.", funcName); return; } typeAlignment = 4; break; default: isTypeValid = false; break; } if (!isTypeValid) { ErrorInvalidEnumArg(funcName, "type", type); return; } //// // `alignment` should always be a power of two. MOZ_ASSERT(IsPowerOfTwo(typeAlignment)); const GLsizei typeAlignmentMask = typeAlignment - 1; if (stride & typeAlignmentMask || byteOffset & typeAlignmentMask) { ErrorInvalidOperation("%s: `stride` and `byteOffset` must satisfy the alignment" " requirement of `type`.", funcName); return; } //// const auto& buffer = mBoundArrayBuffer; if (!buffer && byteOffset) { ErrorInvalidOperation("%s: If ARRAY_BUFFER is null, byteOffset must be zero.", funcName); return; } //// gl->MakeCurrent(); if (isFuncInt) { gl->fVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(byteOffset)); } else { gl->fVertexAttribPointer(index, size, type, normalized, stride, reinterpret_cast<void*>(byteOffset)); } WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index]; vd.VertexAttribPointer(isFuncInt, buffer, size, type, normalized, stride, byteOffset); InvalidateBufferFetching(); }
bool WebGLContext::DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset, GLsizei primcount, const char* info, GLuint* out_upperBound) { if (count < 0 || byteOffset < 0) { ErrorInvalidValue("%s: negative count or offset", info); return false; } if (primcount < 0) { ErrorInvalidValue("%s: negative primcount", info); return false; } if (!ValidateStencilParamsForDrawCall()) { return false; } // If count is 0, there's nothing to do. if (count == 0 || primcount == 0) return false; uint8_t bytesPerElem = 0; switch (type) { case LOCAL_GL_UNSIGNED_BYTE: bytesPerElem = 1; break; case LOCAL_GL_UNSIGNED_SHORT: bytesPerElem = 2; break; case LOCAL_GL_UNSIGNED_INT: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { bytesPerElem = 4; } break; } if (!bytesPerElem) { ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", info, type); return false; } if (byteOffset % bytesPerElem != 0) { ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`", info); return false; } const GLsizei first = byteOffset / bytesPerElem; const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count); if (!checked_byteCount.isValid()) { ErrorInvalidValue("%s: overflow in byteCount", info); return false; } // Any checks below this depend on a program being available. if (!mCurrentProgram) { ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info); return false; } if (!mBoundVertexArray->mElementArrayBuffer) { ErrorInvalidOperation("%s: must have element array buffer binding", info); return false; } WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer; if (!elemArrayBuffer.ByteLength()) { ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info); return false; } CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset; if (!checked_neededByteCount.isValid()) { ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info); return false; } if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) { ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info); return false; } if (!ValidateBufferFetching(info)) return false; if (!mMaxFetchedVertices || !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound)) { ErrorInvalidOperation( "%s: bound vertex attribute buffers do not have sufficient " "size for given indices from the bound element array", info); return false; } if (uint32_t(primcount) > mMaxFetchedInstances) { ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info); return false; } // Bug 1008310 - Check if buffer has been used with a different previous type if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) { GenerateWarning("%s: bound element array buffer previously used with a type other than " "%s, this will affect performance.", info, WebGLContext::EnumName(type)); } MOZ_ASSERT(gl->IsCurrent()); if (mBoundDrawFramebuffer) { if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(info)) return false; } else { ClearBackbufferIfNeeded(); } if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) { return false; } return true; }
bool WebGLContext::InitAndValidateGL() { if (!gl) return false; // Unconditionally create a new format usage authority. This is // important when restoring contexts and extensions need to add // formats back into the authority. mFormatUsage = CreateFormatUsage(gl); if (!mFormatUsage) return false; GLenum error = gl->fGetError(); if (error != LOCAL_GL_NO_ERROR) { GenerateWarning("GL error 0x%x occurred during OpenGL context" " initialization, before WebGL initialization!", error); return false; } mMinCapability = gfxPrefs::WebGLMinCapabilityMode(); mDisableExtensions = gfxPrefs::WebGLDisableExtensions(); mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure(); mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground(); mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible(); if (MinCapabilityMode()) mDisableFragHighP = true; // These are the default values, see 6.2 State tables in the // OpenGL ES 2.0.25 spec. mColorWriteMask[0] = 1; mColorWriteMask[1] = 1; mColorWriteMask[2] = 1; mColorWriteMask[3] = 1; mDepthWriteMask = 1; mColorClearValue[0] = 0.f; mColorClearValue[1] = 0.f; mColorClearValue[2] = 0.f; mColorClearValue[3] = 0.f; mDepthClearValue = 1.f; mStencilClearValue = 0; mStencilRefFront = 0; mStencilRefBack = 0; /* // Technically, we should be setting mStencil[...] values to // `allOnes`, but either ANGLE breaks or the SGX540s on Try break. GLuint stencilBits = 0; gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits); GLuint allOnes = ~(UINT32_MAX << stencilBits); mStencilValueMaskFront = allOnes; mStencilValueMaskBack = allOnes; mStencilWriteMaskFront = allOnes; mStencilWriteMaskBack = allOnes; */ gl->GetUIntegerv(LOCAL_GL_STENCIL_VALUE_MASK, &mStencilValueMaskFront); gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_VALUE_MASK, &mStencilValueMaskBack); gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &mStencilWriteMaskFront); gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &mStencilWriteMaskBack); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK, mStencilValueMaskFront); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK, mStencilWriteMaskFront); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK, mStencilWriteMaskBack); mDitherEnabled = true; mRasterizerDiscardEnabled = false; mScissorTestEnabled = false; // Bindings, etc. mActiveTexture = 0; mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK; mEmitContextLostErrorOnce = true; mWebGLError = LOCAL_GL_NO_ERROR; mUnderlyingGLError = LOCAL_GL_NO_ERROR; mBound2DTextures.Clear(); mBoundCubeMapTextures.Clear(); mBound3DTextures.Clear(); mBound2DArrayTextures.Clear(); mBoundSamplers.Clear(); mBoundArrayBuffer = nullptr; mBoundTransformFeedbackBuffer = nullptr; mCurrentProgram = nullptr; mBoundDrawFramebuffer = nullptr; mBoundReadFramebuffer = nullptr; mBoundRenderbuffer = nullptr; MakeContextCurrent(); // For OpenGL compat. profiles, we always keep vertex attrib 0 array enabled. if (gl->IsCompatibilityProfile()) gl->fEnableVertexAttribArray(0); if (MinCapabilityMode()) mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS; else gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs); if (mGLMaxVertexAttribs < 8) { GenerateWarning("GL_MAX_VERTEX_ATTRIBS: %d is < 8!", mGLMaxVertexAttribs); return false; } // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware, // even though the hardware supports much more. The // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value. if (MinCapabilityMode()) mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; else gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits); if (mGLMaxTextureUnits < 8) { GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!", mGLMaxTextureUnits); return false; } mBound2DTextures.SetLength(mGLMaxTextureUnits); mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits); mBound3DTextures.SetLength(mGLMaxTextureUnits); mBound2DArrayTextures.SetLength(mGLMaxTextureUnits); mBoundSamplers.SetLength(mGLMaxTextureUnits); //////////////// if (MinCapabilityMode()) { mImplMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE; mImplMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE; mImplMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE; mImplMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE; mImplMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS; mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS; mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; } else { gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize); gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize); gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, (GLint*)&mImplMaxRenderbufferSize); if (!gl->GetPotentialInteger(LOCAL_GL_MAX_3D_TEXTURE_SIZE, (GLint*)&mImplMax3DTextureSize)) mImplMax3DTextureSize = 0; if (!gl->GetPotentialInteger(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, (GLint*)&mImplMaxArrayTextureLayers)) mImplMaxArrayTextureLayers = 0; gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits); gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits); } // If we don't support a target, its max size is 0. We should only floor-to-POT if the // value if it's non-zero. (NB log2(0) is -Inf, so zero isn't an integer power-of-two) const auto fnFloorPOTIfSupported = [](uint32_t& val) { if (val) { val = FloorPOT(val); } }; fnFloorPOTIfSupported(mImplMaxTextureSize); fnFloorPOTIfSupported(mImplMaxCubeMapTextureSize); fnFloorPOTIfSupported(mImplMaxRenderbufferSize); fnFloorPOTIfSupported(mImplMax3DTextureSize); fnFloorPOTIfSupported(mImplMaxArrayTextureLayers); //////////////// mGLMaxColorAttachments = 1; mGLMaxDrawBuffers = 1; gl->GetPotentialInteger(LOCAL_GL_MAX_COLOR_ATTACHMENTS, (GLint*)&mGLMaxColorAttachments); gl->GetPotentialInteger(LOCAL_GL_MAX_DRAW_BUFFERS, (GLint*)&mGLMaxDrawBuffers); if (MinCapabilityMode()) { mGLMaxColorAttachments = std::min(mGLMaxColorAttachments, kMinMaxColorAttachments); mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, kMinMaxDrawBuffers); } if (IsWebGL2()) { mImplMaxColorAttachments = mGLMaxColorAttachments; mImplMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mImplMaxColorAttachments); } else { mImplMaxColorAttachments = 1; mImplMaxDrawBuffers = 1; } //////////////// if (MinCapabilityMode()) { mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS; mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS; mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS; } else { if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors); gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors); gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors); } else { gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors); mGLMaxFragmentUniformVectors /= 4; gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors); mGLMaxVertexUniformVectors /= 4; /* We are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS * and GL_MAX_FRAGMENT_INPUT_COMPONENTS, however these constants * only entered the OpenGL standard at OpenGL 3.2. So we will try * reading, and check OpenGL error for INVALID_ENUM. * * On the public_webgl list, "problematic GetParameter pnames" * thread, the following formula was given: * maxVaryingVectors = min(GL_MAX_VERTEX_OUTPUT_COMPONENTS, * GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4 */ GLint maxVertexOutputComponents = 0; GLint maxFragmentInputComponents = 0; const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, &maxVertexOutputComponents) && gl->GetPotentialInteger(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, &maxFragmentInputComponents)); if (ok) { mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, maxFragmentInputComponents) / 4; } else { mGLMaxVaryingVectors = 16; // 16 = 64/4, and 64 is the min value for // maxVertexOutputComponents in the OpenGL 3.2 spec. } } } if (gl->IsCompatibilityProfile()) { // gl_PointSize is always available in ES2 GLSL, but has to be // specifically enabled on desktop GLSL. gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE); /* gl_PointCoord is always available in ES2 GLSL and in newer desktop * GLSL versions, but apparently not in OpenGL 2 and apparently not (due * to a driver bug) on certain NVIDIA setups. See: * http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=261472 * * Note that this used to cause crashes on old ATI drivers... Hopefully * not a significant anymore. See bug 602183. */ gl->fEnable(LOCAL_GL_POINT_SPRITE); } #ifdef XP_MACOSX if (gl->WorkAroundDriverBugs() && gl->Vendor() == gl::GLVendor::ATI && !nsCocoaFeatures::IsAtLeastVersion(10,9)) { // The Mac ATI driver, in all known OSX version up to and including // 10.8, renders points sprites upside-down. (Apple bug 11778921) gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN, LOCAL_GL_LOWER_LEFT); } #endif if (gl->IsSupported(gl::GLFeature::seamless_cube_map_opt_in)) { gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS); } // Check the shader validator pref mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator(); // initialize shader translator if (!ShInitialize()) { GenerateWarning("GLSL translator initialization failed!"); return false; } // Mesa can only be detected with the GL_VERSION string, of the form // "2.1 Mesa 7.11.0" const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION)); mIsMesa = strstr(versionStr, "Mesa"); // Notice that the point of calling fGetError here is not only to check for // errors, but also to reset the error flags so that a subsequent WebGL // getError call will give the correct result. error = gl->fGetError(); if (error != LOCAL_GL_NO_ERROR) { GenerateWarning("GL error 0x%x occurred during WebGL context" " initialization!", error); return false; } if (IsWebGL2() && !InitWebGL2()) { // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL. return false; } // Default value for all disabled vertex attributes is [0, 0, 0, 1] mVertexAttribType = MakeUnique<GLenum[]>(mGLMaxVertexAttribs); for (int32_t index = 0; index < mGLMaxVertexAttribs; ++index) { mVertexAttribType[index] = LOCAL_GL_FLOAT; VertexAttrib4f(index, 0, 0, 0, 1); } mDefaultVertexArray = WebGLVertexArray::Create(this); mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs); mBoundVertexArray = mDefaultVertexArray; // OpenGL core profiles remove the default VAO object from version // 4.0.0. We create a default VAO for all core profiles, // regardless of version. // // GL Spec 4.0.0: // (https://www.opengl.org/registry/doc/glspec40.core.20100311.pdf) // in Section E.2.2 "Removed Features", pg 397: "[...] The default // vertex array object (the name zero) is also deprecated. [...]" if (gl->IsCoreProfile()) { MakeContextCurrent(); mDefaultVertexArray->GenVertexArray(); mDefaultVertexArray->BindVertexArray(); } mPixelStore_FlipY = false; mPixelStore_PremultiplyAlpha = false; mPixelStore_ColorspaceConversion = BROWSER_DEFAULT_WEBGL; // GLES 3.0.4, p259: mPixelStore_UnpackImageHeight = 0; mPixelStore_UnpackSkipImages = 0; mPixelStore_UnpackRowLength = 0; mPixelStore_UnpackSkipRows = 0; mPixelStore_UnpackSkipPixels = 0; mPixelStore_UnpackAlignment = 4; mPixelStore_PackRowLength = 0; mPixelStore_PackSkipRows = 0; mPixelStore_PackSkipPixels = 0; mPixelStore_PackAlignment = 4; return true; }
void WebGLContext::AssertCachedBindings() { #ifdef DEBUG MakeContextCurrent(); GetAndFlushUnderlyingGLErrors(); if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound); } // Framebuffers if (IsWebGL2()) { GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, bound); bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_READ_FRAMEBUFFER_BINDING, bound); } else { MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer); GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound); } GLint stencilBits = 0; if (GetStencilBits(&stencilBits)) { // Depends on current draw framebuffer. const GLuint stencilRefMask = (1 << stencilBits) - 1; AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF, stencilRefMask, mStencilRefFront); AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack); } // Program GLuint bound = mCurrentProgram ? mCurrentProgram->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound); // Textures GLenum activeTexture = mActiveTexture + LOCAL_GL_TEXTURE0; AssertUintParamCorrect(gl, LOCAL_GL_ACTIVE_TEXTURE, activeTexture); WebGLTexture* curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_2D); bound = curTex ? curTex->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_2D, bound); curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_CUBE_MAP); bound = curTex ? curTex->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_CUBE_MAP, bound); // Buffers bound = mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_ARRAY_BUFFER_BINDING, bound); MOZ_ASSERT(mBoundVertexArray); WebGLBuffer* curBuff = mBoundVertexArray->mElementArrayBuffer; bound = curBuff ? curBuff->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING, bound); MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors()); #endif // We do not check the renderbuffer binding, because we never rely on it matching. }
bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const { if (mDisableExtensions) return false; // In alphabetical order switch (ext) { // ANGLE_ case WebGLExtensionID::ANGLE_instanced_arrays: return WebGLExtensionInstancedArrays::IsSupported(this); // EXT_ case WebGLExtensionID::EXT_blend_minmax: return WebGLExtensionBlendMinMax::IsSupported(this); case WebGLExtensionID::EXT_color_buffer_half_float: return WebGLExtensionColorBufferHalfFloat::IsSupported(this); case WebGLExtensionID::EXT_frag_depth: return WebGLExtensionFragDepth::IsSupported(this); case WebGLExtensionID::EXT_shader_texture_lod: return gl->IsExtensionSupported(gl::GLContext::EXT_shader_texture_lod); case WebGLExtensionID::EXT_sRGB: return WebGLExtensionSRGB::IsSupported(this); case WebGLExtensionID::EXT_texture_filter_anisotropic: return gl->IsExtensionSupported(gl::GLContext::EXT_texture_filter_anisotropic); // OES_ case WebGLExtensionID::OES_element_index_uint: return gl->IsSupported(gl::GLFeature::element_index_uint); case WebGLExtensionID::OES_standard_derivatives: return gl->IsSupported(gl::GLFeature::standard_derivatives); case WebGLExtensionID::OES_texture_float: return gl->IsSupported(gl::GLFeature::texture_float); case WebGLExtensionID::OES_texture_float_linear: return gl->IsSupported(gl::GLFeature::texture_float_linear); case WebGLExtensionID::OES_texture_half_float: // If we have Feature::texture_half_float, we must not be on ES2 // and need to translate HALF_FLOAT_OES -> HALF_FLOAT. We do that // right before making the relevant calls. return gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float) || gl->IsSupported(gl::GLFeature::texture_half_float); case WebGLExtensionID::OES_texture_half_float_linear: return gl->IsSupported(gl::GLFeature::texture_half_float_linear); case WebGLExtensionID::OES_vertex_array_object: return true; // WEBGL_ case WebGLExtensionID::WEBGL_color_buffer_float: return WebGLExtensionColorBufferFloat::IsSupported(this); case WebGLExtensionID::WEBGL_compressed_texture_atc: return gl->IsExtensionSupported(gl::GLContext::AMD_compressed_ATC_texture); case WebGLExtensionID::WEBGL_compressed_texture_etc1: return gl->IsExtensionSupported(gl::GLContext::OES_compressed_ETC1_RGB8_texture); case WebGLExtensionID::WEBGL_compressed_texture_pvrtc: return gl->IsExtensionSupported(gl::GLContext::IMG_texture_compression_pvrtc); case WebGLExtensionID::WEBGL_compressed_texture_s3tc: if (gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc)) return true; return gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_dxt1) && gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt3) && gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt5); case WebGLExtensionID::WEBGL_debug_renderer_info: return Preferences::GetBool("webgl.enable-debug-renderer-info", false); case WebGLExtensionID::WEBGL_depth_texture: // WEBGL_depth_texture supports DEPTH_STENCIL textures if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil)) return false; return gl->IsSupported(gl::GLFeature::depth_texture) || gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture); case WebGLExtensionID::WEBGL_draw_buffers: return WebGLExtensionDrawBuffers::IsSupported(this); case WebGLExtensionID::WEBGL_lose_context: // We always support this extension. return true; default: // For warnings-as-errors. break; } if (gfxPrefs::WebGLDraftExtensionsEnabled() || IsWebGL2()) { switch (ext) { case WebGLExtensionID::EXT_disjoint_timer_query: return WebGLExtensionDisjointTimerQuery::IsSupported(this); default: // For warnings-as-errors. break; } } return false; }
void WebGLContext::AssertCachedGlobalState() { #ifdef DEBUG MakeContextCurrent(); GetAndFlushUnderlyingGLErrors(); //////////////// // Draw state MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DEPTH_TEST) == mDepthTestEnabled); MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DITHER) == mDitherEnabled); MOZ_ASSERT_IF(IsWebGL2(), gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled); MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled); MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_STENCIL_TEST) == mStencilTestEnabled); realGLboolean colorWriteMask[4] = {0, 0, 0, 0}; gl->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask); MOZ_ASSERT(colorWriteMask[0] == mColorWriteMask[0] && colorWriteMask[1] == mColorWriteMask[1] && colorWriteMask[2] == mColorWriteMask[2] && colorWriteMask[3] == mColorWriteMask[3]); GLfloat colorClearValue[4] = {0.0f, 0.0f, 0.0f, 0.0f}; gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue); MOZ_ASSERT(IsCacheCorrect(mColorClearValue[0], colorClearValue[0]) && IsCacheCorrect(mColorClearValue[1], colorClearValue[1]) && IsCacheCorrect(mColorClearValue[2], colorClearValue[2]) && IsCacheCorrect(mColorClearValue[3], colorClearValue[3])); realGLboolean depthWriteMask = 0; gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask); MOZ_ASSERT(depthWriteMask == mDepthWriteMask); GLfloat depthClearValue = 0.0f; gl->fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue); MOZ_ASSERT(IsCacheCorrect(mDepthClearValue, depthClearValue)); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_CLEAR_VALUE, mStencilClearValue); // GLES 3.0.4, $4.1.4, p177: // [...] the front and back stencil mask are both set to the value `2^s - 1`, where // `s` is greater than or equal to the number of bits in the deepest stencil buffer // supported by the GL implementation. const int maxStencilBits = 8; const GLuint maxStencilBitsMask = (1 << maxStencilBits) - 1; AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK, maxStencilBitsMask, mStencilValueMaskFront); AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, maxStencilBitsMask, mStencilValueMaskBack); AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK, maxStencilBitsMask, mStencilWriteMaskFront); AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK, maxStencilBitsMask, mStencilWriteMaskBack); // Viewport GLint int4[4] = {0, 0, 0, 0}; gl->fGetIntegerv(LOCAL_GL_VIEWPORT, int4); MOZ_ASSERT(int4[0] == mViewportX && int4[1] == mViewportY && int4[2] == mViewportWidth && int4[3] == mViewportHeight); AssertUintParamCorrect(gl, LOCAL_GL_PACK_ALIGNMENT, mPixelStore_PackAlignment); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ALIGNMENT, mPixelStore_UnpackAlignment); if (IsWebGL2()) { AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_IMAGE_HEIGHT, mPixelStore_UnpackImageHeight); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_IMAGES , mPixelStore_UnpackSkipImages); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ROW_LENGTH , mPixelStore_UnpackRowLength); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_ROWS , mPixelStore_UnpackSkipRows); AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_PIXELS , mPixelStore_UnpackSkipPixels); AssertUintParamCorrect(gl, LOCAL_GL_PACK_ROW_LENGTH , mPixelStore_PackRowLength); AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_ROWS , mPixelStore_PackSkipRows); AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_PIXELS , mPixelStore_PackSkipPixels); } MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors()); #endif }
bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const { if (mDisableExtensions) return false; // Extensions for both WebGL 1 and 2. switch (ext) { // In alphabetical order // EXT_ case WebGLExtensionID::EXT_disjoint_timer_query: return WebGLExtensionDisjointTimerQuery::IsSupported(this); case WebGLExtensionID::EXT_texture_filter_anisotropic: return gl->IsExtensionSupported(gl::GLContext::EXT_texture_filter_anisotropic); // OES_ case WebGLExtensionID::OES_texture_float_linear: return gl->IsSupported(gl::GLFeature::texture_float_linear); // WEBGL_ case WebGLExtensionID::WEBGL_compressed_texture_astc: return WebGLExtensionCompressedTextureASTC::IsSupported(this); case WebGLExtensionID::WEBGL_compressed_texture_atc: return gl->IsExtensionSupported(gl::GLContext::AMD_compressed_ATC_texture); case WebGLExtensionID::WEBGL_compressed_texture_etc: return gl->IsSupported(gl::GLFeature::ES3_compatibility) && !gl->IsANGLE(); case WebGLExtensionID::WEBGL_compressed_texture_etc1: return gl->IsExtensionSupported(gl::GLContext::OES_compressed_ETC1_RGB8_texture) && !gl->IsANGLE(); case WebGLExtensionID::WEBGL_compressed_texture_pvrtc: return gl->IsExtensionSupported(gl::GLContext::IMG_texture_compression_pvrtc); case WebGLExtensionID::WEBGL_compressed_texture_s3tc: return WebGLExtensionCompressedTextureS3TC::IsSupported(this); case WebGLExtensionID::WEBGL_compressed_texture_s3tc_srgb: return WebGLExtensionCompressedTextureS3TC_SRGB::IsSupported(this); case WebGLExtensionID::WEBGL_debug_renderer_info: return Preferences::GetBool("webgl.enable-debug-renderer-info", false); case WebGLExtensionID::WEBGL_debug_shaders: return !nsContentUtils::ShouldResistFingerprinting(); case WebGLExtensionID::WEBGL_lose_context: // We always support this extension. return true; default: // For warnings-as-errors. break; } if (IsWebGL2()) { // WebGL2-only extensions switch (ext) { // EXT_ case WebGLExtensionID::EXT_color_buffer_float: return WebGLExtensionEXTColorBufferFloat::IsSupported(this); default: // For warnings-as-errors. break; } } else { // WebGL1-only extensions switch (ext) { // ANGLE_ case WebGLExtensionID::ANGLE_instanced_arrays: return WebGLExtensionInstancedArrays::IsSupported(this); // EXT_ case WebGLExtensionID::EXT_blend_minmax: return WebGLExtensionBlendMinMax::IsSupported(this); case WebGLExtensionID::EXT_color_buffer_half_float: return WebGLExtensionColorBufferHalfFloat::IsSupported(this); case WebGLExtensionID::EXT_frag_depth: return WebGLExtensionFragDepth::IsSupported(this); case WebGLExtensionID::EXT_shader_texture_lod: return gl->IsSupported(gl::GLFeature::shader_texture_lod); case WebGLExtensionID::EXT_sRGB: return WebGLExtensionSRGB::IsSupported(this); // OES_ case WebGLExtensionID::OES_element_index_uint: return gl->IsSupported(gl::GLFeature::element_index_uint); case WebGLExtensionID::OES_standard_derivatives: return gl->IsSupported(gl::GLFeature::standard_derivatives); case WebGLExtensionID::OES_texture_float: return WebGLExtensionTextureFloat::IsSupported(this); case WebGLExtensionID::OES_texture_half_float: return WebGLExtensionTextureHalfFloat::IsSupported(this); case WebGLExtensionID::OES_texture_half_float_linear: return gl->IsSupported(gl::GLFeature::texture_half_float_linear); case WebGLExtensionID::OES_vertex_array_object: return true; // WEBGL_ case WebGLExtensionID::WEBGL_color_buffer_float: return WebGLExtensionColorBufferFloat::IsSupported(this); case WebGLExtensionID::WEBGL_depth_texture: // WEBGL_depth_texture supports DEPTH_STENCIL textures if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil)) return false; return gl->IsSupported(gl::GLFeature::depth_texture) || gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture); case WebGLExtensionID::WEBGL_draw_buffers: return WebGLExtensionDrawBuffers::IsSupported(this); default: // For warnings-as-errors. break; } if (gfxPrefs::WebGLDraftExtensionsEnabled()) { /* switch (ext) { default: // For warnings-as-errors. break; } */ } } return false; }
bool WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vertCount, GLenum type, WebGLintptr byteOffset, GLsizei instanceCount) { if (!ValidateDrawModeEnum(mode, funcName)) return false; if (mBoundTransformFeedback && mBoundTransformFeedback->mIsActive && !mBoundTransformFeedback->mIsPaused) { ErrorInvalidOperation("%s: DrawElements* functions are incompatible with" " transform feedback.", funcName); return false; } if (!ValidateNonNegative(funcName, "vertCount", vertCount) || !ValidateNonNegative(funcName, "byteOffset", byteOffset) || !ValidateNonNegative(funcName, "instanceCount", instanceCount)) { return false; } if (!ValidateStencilParamsForDrawCall()) return false; if (!vertCount || !instanceCount) return false; // No error, just early out. uint8_t bytesPerElem = 0; switch (type) { case LOCAL_GL_UNSIGNED_BYTE: bytesPerElem = 1; break; case LOCAL_GL_UNSIGNED_SHORT: bytesPerElem = 2; break; case LOCAL_GL_UNSIGNED_INT: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { bytesPerElem = 4; } break; } if (!bytesPerElem) { ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", funcName, type); return false; } if (byteOffset % bytesPerElem != 0) { ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`", funcName); return false; } //// if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); if (mPrimRestartTypeBytes != bytesPerElem) { mPrimRestartTypeBytes = bytesPerElem; const uint32_t ones = UINT32_MAX >> (32 - 8*mPrimRestartTypeBytes); gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART); gl->fPrimitiveRestartIndex(ones); }
JS::Value WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname, ErrorResult& rv) { const char funcName[] = "getVertexAttrib"; if (IsContextLost()) return JS::NullValue(); if (!ValidateAttribIndex(index, funcName)) return JS::NullValue(); MOZ_ASSERT(mBoundVertexArray); MakeContextCurrent(); switch (pname) { case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].mBuf.get(), rv); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE: return JS::Int32Value(mBoundVertexArray->mAttribs[index].Stride()); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE: return JS::Int32Value(mBoundVertexArray->mAttribs[index].Size()); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE: return JS::Int32Value(mBoundVertexArray->mAttribs[index].Type()); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER: if (IsWebGL2()) return JS::BooleanValue(mBoundVertexArray->mAttribs[index].IntegerFunc()); break; case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)) { return JS::Int32Value(mBoundVertexArray->mAttribs[index].mDivisor); } break; case LOCAL_GL_CURRENT_VERTEX_ATTRIB: { JS::RootedObject obj(cx); switch (mGenericVertexAttribTypes[index]) { case LOCAL_GL_FLOAT: obj = GetVertexAttribFloat32Array(cx, index); break; case LOCAL_GL_INT: obj = GetVertexAttribInt32Array(cx, index); break; case LOCAL_GL_UNSIGNED_INT: obj = GetVertexAttribUint32Array(cx, index); break; } if (!obj) rv.Throw(NS_ERROR_OUT_OF_MEMORY); return JS::ObjectOrNullValue(obj); } case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED: return JS::BooleanValue(mBoundVertexArray->mAttribs[index].mEnabled); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: return JS::BooleanValue(mBoundVertexArray->mAttribs[index].Normalized()); default: break; } ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname); return JS::NullValue(); }
bool WebGLContext::InitWebGL2(FailureReason* const out_failReason) { MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!"); std::vector<gl::GLFeature> missingList; const auto fnGatherMissing = [&](gl::GLFeature cur) { if (!gl->IsSupported(cur)) { missingList.push_back(cur); } }; const auto fnGatherMissing2 = [&](gl::GLFeature main, gl::GLFeature alt) { if (!gl->IsSupported(main) && !gl->IsSupported(alt)) { missingList.push_back(main); } }; //// for (const auto& cur : kRequiredFeatures) { fnGatherMissing(cur); } // On desktop, we fake occlusion_query_boolean with occlusion_query if // necessary. (See WebGL2ContextQueries.cpp) fnGatherMissing2(gl::GLFeature::occlusion_query_boolean, gl::GLFeature::occlusion_query); #ifdef XP_MACOSX // On OSX, GL core profile is used. This requires texture swizzle // support to emulate legacy texture formats: ALPHA, LUMINANCE, // and LUMINANCE_ALPHA. fnGatherMissing(gl::GLFeature::texture_swizzle); #endif fnGatherMissing2(gl::GLFeature::prim_restart_fixed, gl::GLFeature::prim_restart); //// if (!missingList.empty()) { nsAutoCString exts; for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) { exts.AppendLiteral("\n "); exts.Append(gl::GLContext::GetFeatureName(*itr)); } const nsPrintfCString reason("WebGL 2 requires support for the following" " features: %s", exts.BeginReading()); *out_failReason = FailureReason("FEATURE_FAILURE_WEBGL2_OCCL", reason); return false; } // we initialise WebGL 2 related stuff. gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &mGLMaxTransformFeedbackSeparateAttribs); gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS, &mGLMaxUniformBufferBindings); mIndexedUniformBufferBindings.resize(mGLMaxUniformBufferBindings); mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; gl->fGenTransformFeedbacks(1, &mEmptyTFO); //// if (!gl->IsGLES()) { // Desktop OpenGL requires the following to be enabled in order to // support sRGB operations on framebuffers. gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT); } if (gl->IsSupported(gl::GLFeature::prim_restart_fixed)) { gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART_FIXED_INDEX); } else { MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart)); } ////// return true; }
bool WebGLContext::InitWebGL2() { MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!"); // check OpenGL features if (!gl->IsSupported(gl::GLFeature::occlusion_query) && !gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) { // On desktop, we fake occlusion_query_boolean with occlusion_query if // necessary. (See WebGL2ContextQueries.cpp) GenerateWarning("WebGL 2 unavailable. Requires occlusion queries."); return false; } std::vector<gl::GLFeature> missingList; for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) { if (!gl->IsSupported(kRequiredFeatures[i])) missingList.push_back(kRequiredFeatures[i]); } #ifdef XP_MACOSX // On OSX, GL core profile is used. This requires texture swizzle // support to emulate legacy texture formats: ALPHA, LUMINANCE, // and LUMINANCE_ALPHA. if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) missingList.push_back(gl::GLFeature::texture_swizzle); #endif if (missingList.size()) { nsAutoCString exts; for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) { exts.AppendLiteral("\n "); exts.Append(gl::GLContext::GetFeatureName(*itr)); } GenerateWarning("WebGL 2 unavailable. The following required features are" " unavailible: %s", exts.BeginReading()); return false; } // we initialise WebGL 2 related stuff. gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &mGLMaxTransformFeedbackSeparateAttribs); gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS, &mGLMaxUniformBufferBindings); mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs); mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings); mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; if (!gl->IsGLES()) { // Desktop OpenGL requires the following to be enabled in order to // support sRGB operations on framebuffers. gl->MakeCurrent(); gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT); } return true; }