/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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 "WebGLContextUtils.h" #include "WebGLContext.h" #include "GLContext.h" #include "jsapi.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/gfx/Logging.h" #include "mozilla/Preferences.h" #include "mozilla/Sprintf.h" #include "nsIDOMEvent.h" #include "nsIScriptSecurityManager.h" #include "nsIVariant.h" #include "nsPrintfCString.h" #include "nsServiceManagerUtils.h" #include #include "WebGLBuffer.h" #include "WebGLExtensions.h" #include "WebGLFramebuffer.h" #include "WebGLProgram.h" #include "WebGLTexture.h" #include "WebGLVertexArray.h" namespace mozilla { TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget) { switch (texImageTarget.get()) { case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return LOCAL_GL_TEXTURE_CUBE_MAP; default: return texImageTarget.get(); } } JS::Value StringValue(JSContext* cx, const char* chars, ErrorResult& rv) { JSString* str = JS_NewStringCopyZ(cx, chars); if (!str) { rv.Throw(NS_ERROR_OUT_OF_MEMORY); return JS::NullValue(); } return JS::StringValue(str); } void WebGLContext::GenerateWarning(const char* fmt, ...) const { va_list ap; va_start(ap, fmt); GenerateWarning(fmt, ap); va_end(ap); } void WebGLContext::GenerateWarning(const char* fmt, va_list ap) const { if (!ShouldGenerateWarnings()) return; mAlreadyGeneratedWarnings++; char buf[1024]; VsprintfLiteral(buf, fmt, ap); // no need to print to stderr, as JS_ReportWarning takes care of this for us. if (!mCanvasElement) { return; } dom::AutoJSAPI api; if (!api.Init(mCanvasElement->OwnerDoc()->GetScopeObject())) { return; } JSContext* cx = api.cx(); JS_ReportWarningASCII(cx, "WebGL warning: %s", buf); if (!ShouldGenerateWarnings()) { JS_ReportWarningASCII(cx, "WebGL: No further warnings will be reported for" " this WebGL context." " (already reported %d warnings)", mAlreadyGeneratedWarnings); } } bool WebGLContext::ShouldGenerateWarnings() const { if (mMaxWarnings == -1) return true; return mAlreadyGeneratedWarnings < mMaxWarnings; } void WebGLContext::GeneratePerfWarning(const char* fmt, ...) const { if (!ShouldGeneratePerfWarnings()) return; if (!mCanvasElement) return; dom::AutoJSAPI api; if (!api.Init(mCanvasElement->OwnerDoc()->GetScopeObject())) return; JSContext* cx = api.cx(); //// va_list ap; va_start(ap, fmt); char buf[1024]; VsprintfLiteral(buf, fmt, ap); va_end(ap); //// JS_ReportWarningASCII(cx, "WebGL perf warning: %s", buf); mNumPerfWarnings++; if (!ShouldGeneratePerfWarnings()) { JS_ReportWarningASCII(cx, "WebGL: After reporting %u, no further perf warnings will" " be reported for this WebGL context.", uint32_t(mNumPerfWarnings)); } } void WebGLContext::SynthesizeGLError(GLenum err) const { /* ES2 section 2.5 "GL Errors" states that implementations can have * multiple 'flags', as errors might be caught in different parts of * a distributed implementation. * We're signing up as a distributed implementation here, with * separate flags for WebGL and the underlying GLContext. */ if (!mWebGLError) mWebGLError = err; } void WebGLContext::SynthesizeGLError(GLenum err, const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(err); } void WebGLContext::ErrorInvalidEnum(const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_INVALID_ENUM); } void WebGLContext::ErrorInvalidEnumInfo(const char* info, GLenum enumValue) const { nsCString name; EnumName(enumValue, &name); return ErrorInvalidEnum("%s: invalid enum value %s", info, name.BeginReading()); } void WebGLContext::ErrorInvalidEnumInfo(const char* info, const char* funcName, GLenum enumValue) const { nsCString name; EnumName(enumValue, &name); ErrorInvalidEnum("%s: %s: Invalid enum: 0x%04x (%s).", funcName, info, enumValue, name.BeginReading()); } void WebGLContext::ErrorInvalidOperation(const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_INVALID_OPERATION); } void WebGLContext::ErrorInvalidValue(const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_INVALID_VALUE); } void WebGLContext::ErrorInvalidFramebufferOperation(const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); } void WebGLContext::ErrorOutOfMemory(const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY); } void WebGLContext::ErrorImplementationBug(const char* fmt, ...) const { const nsPrintfCString warning("Implementation bug, please file at %s! %s", "https://bugzilla.mozilla.org/", fmt); va_list va; va_start(va, fmt); GenerateWarning(warning.BeginReading(), va); va_end(va); MOZ_ASSERT(false, "WebGLContext::ErrorImplementationBug"); NS_ERROR("WebGLContext::ErrorImplementationBug"); return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY); } /*static*/ const char* WebGLContext::ErrorName(GLenum error) { switch(error) { case LOCAL_GL_INVALID_ENUM: return "INVALID_ENUM"; case LOCAL_GL_INVALID_OPERATION: return "INVALID_OPERATION"; case LOCAL_GL_INVALID_VALUE: return "INVALID_VALUE"; case LOCAL_GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY"; case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID_FRAMEBUFFER_OPERATION"; case LOCAL_GL_NO_ERROR: return "NO_ERROR"; default: MOZ_ASSERT(false); return "[unknown WebGL error]"; } } // This version is fallible and will return nullptr if unrecognized. static const char* GetEnumName(GLenum val) { switch (val) { #define XX(x) case LOCAL_GL_##x: return #x XX(NONE); XX(ALPHA); XX(ATC_RGB); XX(ATC_RGBA_EXPLICIT_ALPHA); XX(ATC_RGBA_INTERPOLATED_ALPHA); XX(COMPRESSED_RGBA_PVRTC_2BPPV1); XX(COMPRESSED_RGBA_PVRTC_4BPPV1); XX(COMPRESSED_RGBA_S3TC_DXT1_EXT); XX(COMPRESSED_RGBA_S3TC_DXT3_EXT); XX(COMPRESSED_RGBA_S3TC_DXT5_EXT); XX(COMPRESSED_RGB_PVRTC_2BPPV1); XX(COMPRESSED_RGB_PVRTC_4BPPV1); XX(COMPRESSED_RGB_S3TC_DXT1_EXT); XX(DEPTH_ATTACHMENT); XX(DEPTH_COMPONENT); XX(DEPTH_COMPONENT16); XX(DEPTH_COMPONENT32); XX(DEPTH_STENCIL); XX(DEPTH24_STENCIL8); XX(DRAW_FRAMEBUFFER); XX(ETC1_RGB8_OES); XX(FLOAT); XX(INT); XX(FRAMEBUFFER); XX(HALF_FLOAT); XX(LUMINANCE); XX(LUMINANCE_ALPHA); XX(READ_FRAMEBUFFER); XX(RGB); XX(RGB16F); XX(RGB32F); XX(RGBA); XX(RGBA16F); XX(RGBA32F); XX(SRGB); XX(SRGB_ALPHA); XX(TEXTURE_2D); XX(TEXTURE_3D); XX(TEXTURE_CUBE_MAP); XX(TEXTURE_CUBE_MAP_NEGATIVE_X); XX(TEXTURE_CUBE_MAP_NEGATIVE_Y); XX(TEXTURE_CUBE_MAP_NEGATIVE_Z); XX(TEXTURE_CUBE_MAP_POSITIVE_X); XX(TEXTURE_CUBE_MAP_POSITIVE_Y); XX(TEXTURE_CUBE_MAP_POSITIVE_Z); XX(UNSIGNED_BYTE); XX(UNSIGNED_INT); XX(UNSIGNED_INT_24_8); XX(UNSIGNED_SHORT); XX(UNSIGNED_SHORT_4_4_4_4); XX(UNSIGNED_SHORT_5_5_5_1); XX(UNSIGNED_SHORT_5_6_5); XX(READ_BUFFER); XX(UNPACK_ROW_LENGTH); XX(UNPACK_SKIP_ROWS); XX(UNPACK_SKIP_PIXELS); XX(PACK_ROW_LENGTH); XX(PACK_SKIP_ROWS); XX(PACK_SKIP_PIXELS); XX(COLOR); XX(DEPTH); XX(STENCIL); XX(RED); XX(RGB8); XX(RGBA8); XX(RGB10_A2); XX(TEXTURE_BINDING_3D); XX(UNPACK_SKIP_IMAGES); XX(UNPACK_IMAGE_HEIGHT); XX(TEXTURE_WRAP_R); XX(MAX_3D_TEXTURE_SIZE); XX(UNSIGNED_INT_2_10_10_10_REV); XX(MAX_ELEMENTS_VERTICES); XX(MAX_ELEMENTS_INDICES); XX(TEXTURE_MIN_LOD); XX(TEXTURE_MAX_LOD); XX(TEXTURE_BASE_LEVEL); XX(TEXTURE_MAX_LEVEL); XX(MIN); XX(MAX); XX(DEPTH_COMPONENT24); XX(MAX_TEXTURE_LOD_BIAS); XX(TEXTURE_COMPARE_MODE); XX(TEXTURE_COMPARE_FUNC); XX(CURRENT_QUERY); XX(QUERY_RESULT); XX(QUERY_RESULT_AVAILABLE); XX(STREAM_READ); XX(STREAM_COPY); XX(STATIC_READ); XX(STATIC_COPY); XX(DYNAMIC_READ); XX(DYNAMIC_COPY); XX(MAX_DRAW_BUFFERS); XX(DRAW_BUFFER0); XX(DRAW_BUFFER1); XX(DRAW_BUFFER2); XX(DRAW_BUFFER3); XX(DRAW_BUFFER4); XX(DRAW_BUFFER5); XX(DRAW_BUFFER6); XX(DRAW_BUFFER7); XX(DRAW_BUFFER8); XX(DRAW_BUFFER9); XX(DRAW_BUFFER10); XX(DRAW_BUFFER11); XX(DRAW_BUFFER12); XX(DRAW_BUFFER13); XX(DRAW_BUFFER14); XX(DRAW_BUFFER15); XX(MAX_FRAGMENT_UNIFORM_COMPONENTS); XX(MAX_VERTEX_UNIFORM_COMPONENTS); XX(SAMPLER_3D); XX(SAMPLER_2D_SHADOW); XX(FRAGMENT_SHADER_DERIVATIVE_HINT); XX(PIXEL_PACK_BUFFER); XX(PIXEL_UNPACK_BUFFER); XX(PIXEL_PACK_BUFFER_BINDING); XX(PIXEL_UNPACK_BUFFER_BINDING); XX(FLOAT_MAT2x3); XX(FLOAT_MAT2x4); XX(FLOAT_MAT3x2); XX(FLOAT_MAT3x4); XX(FLOAT_MAT4x2); XX(FLOAT_MAT4x3); XX(SRGB8); XX(SRGB8_ALPHA8); XX(COMPARE_REF_TO_TEXTURE); XX(VERTEX_ATTRIB_ARRAY_INTEGER); XX(MAX_ARRAY_TEXTURE_LAYERS); XX(MIN_PROGRAM_TEXEL_OFFSET); XX(MAX_PROGRAM_TEXEL_OFFSET); XX(MAX_VARYING_COMPONENTS); XX(TEXTURE_2D_ARRAY); XX(TEXTURE_BINDING_2D_ARRAY); XX(R11F_G11F_B10F); XX(UNSIGNED_INT_10F_11F_11F_REV); XX(RGB9_E5); XX(UNSIGNED_INT_5_9_9_9_REV); XX(TRANSFORM_FEEDBACK_BUFFER_MODE); XX(MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS); XX(TRANSFORM_FEEDBACK_VARYINGS); XX(TRANSFORM_FEEDBACK_BUFFER_START); XX(TRANSFORM_FEEDBACK_BUFFER_SIZE); XX(TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); XX(RASTERIZER_DISCARD); XX(MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS); XX(MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS); XX(INTERLEAVED_ATTRIBS); XX(SEPARATE_ATTRIBS); XX(TRANSFORM_FEEDBACK_BUFFER); XX(TRANSFORM_FEEDBACK_BUFFER_BINDING); XX(RGBA32UI); XX(RGB32UI); XX(RGBA16UI); XX(RGB16UI); XX(RGBA8UI); XX(RGB8UI); XX(RGBA32I); XX(RGB32I); XX(RGBA16I); XX(RGB16I); XX(RGBA8I); XX(RGB8I); XX(RED_INTEGER); XX(RGB_INTEGER); XX(RGBA_INTEGER); XX(SAMPLER_2D_ARRAY); XX(SAMPLER_2D_ARRAY_SHADOW); XX(SAMPLER_CUBE_SHADOW); XX(UNSIGNED_INT_VEC2); XX(UNSIGNED_INT_VEC3); XX(UNSIGNED_INT_VEC4); XX(INT_SAMPLER_2D); XX(INT_SAMPLER_3D); XX(INT_SAMPLER_CUBE); XX(INT_SAMPLER_2D_ARRAY); XX(UNSIGNED_INT_SAMPLER_2D); XX(UNSIGNED_INT_SAMPLER_3D); XX(UNSIGNED_INT_SAMPLER_CUBE); XX(UNSIGNED_INT_SAMPLER_2D_ARRAY); XX(DEPTH_COMPONENT32F); XX(DEPTH32F_STENCIL8); XX(FLOAT_32_UNSIGNED_INT_24_8_REV); XX(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING); XX(FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE); XX(FRAMEBUFFER_ATTACHMENT_RED_SIZE); XX(FRAMEBUFFER_ATTACHMENT_GREEN_SIZE); XX(FRAMEBUFFER_ATTACHMENT_BLUE_SIZE); XX(FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE); XX(FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE); XX(FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE); XX(FRAMEBUFFER_DEFAULT); XX(DEPTH_STENCIL_ATTACHMENT); XX(UNSIGNED_NORMALIZED); XX(DRAW_FRAMEBUFFER_BINDING); XX(READ_FRAMEBUFFER_BINDING); XX(RENDERBUFFER_SAMPLES); XX(FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER); XX(MAX_COLOR_ATTACHMENTS); XX(COLOR_ATTACHMENT0); XX(COLOR_ATTACHMENT1); XX(COLOR_ATTACHMENT2); XX(COLOR_ATTACHMENT3); XX(COLOR_ATTACHMENT4); XX(COLOR_ATTACHMENT5); XX(COLOR_ATTACHMENT6); XX(COLOR_ATTACHMENT7); XX(COLOR_ATTACHMENT8); XX(COLOR_ATTACHMENT9); XX(COLOR_ATTACHMENT10); XX(COLOR_ATTACHMENT11); XX(COLOR_ATTACHMENT12); XX(COLOR_ATTACHMENT13); XX(COLOR_ATTACHMENT14); XX(COLOR_ATTACHMENT15); XX(FRAMEBUFFER_INCOMPLETE_MULTISAMPLE); XX(MAX_SAMPLES); XX(RG); XX(RG_INTEGER); XX(R8); XX(RG8); XX(R16F); XX(R32F); XX(RG16F); XX(RG32F); XX(R8I); XX(R8UI); XX(R16I); XX(R16UI); XX(R32I); XX(R32UI); XX(RG8I); XX(RG8UI); XX(RG16I); XX(RG16UI); XX(RG32I); XX(RG32UI); XX(VERTEX_ARRAY_BINDING); XX(R8_SNORM); XX(RG8_SNORM); XX(RGB8_SNORM); XX(RGBA8_SNORM); XX(SIGNED_NORMALIZED); XX(PRIMITIVE_RESTART_FIXED_INDEX); XX(COPY_READ_BUFFER); XX(COPY_WRITE_BUFFER); XX(UNIFORM_BUFFER); XX(UNIFORM_BUFFER_BINDING); XX(UNIFORM_BUFFER_START); XX(UNIFORM_BUFFER_SIZE); XX(MAX_VERTEX_UNIFORM_BLOCKS); XX(MAX_FRAGMENT_UNIFORM_BLOCKS); XX(MAX_COMBINED_UNIFORM_BLOCKS); XX(MAX_UNIFORM_BUFFER_BINDINGS); XX(MAX_UNIFORM_BLOCK_SIZE); XX(MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS); XX(MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS); XX(UNIFORM_BUFFER_OFFSET_ALIGNMENT); XX(ACTIVE_UNIFORM_BLOCKS); XX(UNIFORM_TYPE); XX(UNIFORM_SIZE); XX(UNIFORM_BLOCK_INDEX); XX(UNIFORM_OFFSET); XX(UNIFORM_ARRAY_STRIDE); XX(UNIFORM_MATRIX_STRIDE); XX(UNIFORM_IS_ROW_MAJOR); XX(UNIFORM_BLOCK_BINDING); XX(UNIFORM_BLOCK_DATA_SIZE); XX(UNIFORM_BLOCK_ACTIVE_UNIFORMS); XX(UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES); XX(UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER); XX(UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER); XX(MAX_VERTEX_OUTPUT_COMPONENTS); XX(MAX_FRAGMENT_INPUT_COMPONENTS); XX(MAX_SERVER_WAIT_TIMEOUT); XX(OBJECT_TYPE); XX(SYNC_CONDITION); XX(SYNC_STATUS); XX(SYNC_FLAGS); XX(SYNC_FENCE); XX(SYNC_GPU_COMMANDS_COMPLETE); XX(UNSIGNALED); XX(SIGNALED); XX(ALREADY_SIGNALED); XX(TIMEOUT_EXPIRED); XX(CONDITION_SATISFIED); XX(WAIT_FAILED); XX(VERTEX_ATTRIB_ARRAY_DIVISOR); XX(ANY_SAMPLES_PASSED); XX(ANY_SAMPLES_PASSED_CONSERVATIVE); XX(SAMPLER_BINDING); XX(RGB10_A2UI); XX(TEXTURE_SWIZZLE_R); XX(TEXTURE_SWIZZLE_G); XX(TEXTURE_SWIZZLE_B); XX(TEXTURE_SWIZZLE_A); XX(GREEN); XX(BLUE); XX(INT_2_10_10_10_REV); XX(TRANSFORM_FEEDBACK); XX(TRANSFORM_FEEDBACK_PAUSED); XX(TRANSFORM_FEEDBACK_ACTIVE); XX(TRANSFORM_FEEDBACK_BINDING); XX(COMPRESSED_R11_EAC); XX(COMPRESSED_SIGNED_R11_EAC); XX(COMPRESSED_RG11_EAC); XX(COMPRESSED_SIGNED_RG11_EAC); XX(COMPRESSED_RGB8_ETC2); XX(COMPRESSED_SRGB8_ETC2); XX(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2); XX(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2); XX(COMPRESSED_RGBA8_ETC2_EAC); XX(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC); XX(TEXTURE_IMMUTABLE_FORMAT); XX(MAX_ELEMENT_INDEX); XX(NUM_SAMPLE_COUNTS); XX(TEXTURE_IMMUTABLE_LEVELS); #undef XX } return nullptr; } /*static*/ void WebGLContext::EnumName(GLenum val, nsCString* out_name) { const char* name = GetEnumName(val); if (name) { *out_name = name; return; } *out_name = nsPrintfCString("", val); } void WebGLContext::ErrorInvalidEnumArg(const char* funcName, const char* argName, GLenum val) const { nsCString enumName; EnumName(val, &enumName); ErrorInvalidEnum("%s: Bad `%s`: %s", funcName, argName, enumName.BeginReading()); } bool IsCompressedTextureFormat(GLenum format) { switch (format) { case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: case LOCAL_GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: case LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: case LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: case LOCAL_GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: case LOCAL_GL_ATC_RGB: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: case LOCAL_GL_ETC1_RGB8_OES: case LOCAL_GL_COMPRESSED_R11_EAC: case LOCAL_GL_COMPRESSED_SIGNED_R11_EAC: case LOCAL_GL_COMPRESSED_RG11_EAC: case LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC: case LOCAL_GL_COMPRESSED_RGB8_ETC2: case LOCAL_GL_COMPRESSED_SRGB8_ETC2: case LOCAL_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: case LOCAL_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: case LOCAL_GL_COMPRESSED_RGBA8_ETC2_EAC: case LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: return true; default: return false; } } bool IsTextureFormatCompressed(TexInternalFormat format) { return IsCompressedTextureFormat(format.get()); } GLenum WebGLContext::GetAndFlushUnderlyingGLErrors() const { // Get and clear GL error in ALL cases. GLenum error = gl->fGetError(); // Only store in mUnderlyingGLError if is hasn't already recorded an // error. if (!mUnderlyingGLError) mUnderlyingGLError = error; return error; } #ifdef DEBUG // For NaNs, etc. static bool IsCacheCorrect(float cached, float actual) { if (IsNaN(cached)) { // GL is allowed to do anything it wants for NaNs, so if we're shadowing // a NaN, then whatever `actual` is might be correct. return true; } return cached == actual; } void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow) { GLuint val = 0; gl->GetUIntegerv(pname, &val); if (val != shadow) { printf_stderr("Failed 0x%04x shadow: Cached 0x%x/%u, should be 0x%x/%u.\n", pname, shadow, shadow, val, val); MOZ_ASSERT(false, "Bad cached value."); } } void AssertMaskedUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint mask, GLuint shadow) { GLuint val = 0; gl->GetUIntegerv(pname, &val); const GLuint valMasked = val & mask; const GLuint shadowMasked = shadow & mask; if (valMasked != shadowMasked) { printf_stderr("Failed 0x%04x shadow: Cached 0x%x/%u, should be 0x%x/%u.\n", pname, shadowMasked, shadowMasked, valMasked, valMasked); MOZ_ASSERT(false, "Bad cached value."); } } #else void AssertUintParamCorrect(gl::GLContext*, GLenum, GLuint) { } #endif void WebGLContext::AssertCachedBindings() const { #ifdef DEBUG GetAndFlushUnderlyingGLErrors(); if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_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. } void WebGLContext::AssertCachedGlobalState() const { #ifdef DEBUG GetAndFlushUnderlyingGLErrors(); //////////////// // 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); GLfloat colorClearValue[4] = {0.0f, 0.0f, 0.0f, 0.0f}; gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue); const bool ok = IsCacheCorrect(mColorClearValue[0], colorClearValue[0]) && IsCacheCorrect(mColorClearValue[1], colorClearValue[1]) && IsCacheCorrect(mColorClearValue[2], colorClearValue[2]) && IsCacheCorrect(mColorClearValue[3], colorClearValue[3]); if (!ok) { gfxCriticalNote << mColorClearValue[0] << " - " << colorClearValue[0] << " = " << (mColorClearValue[0] - colorClearValue[0]) << "\n" << mColorClearValue[1] << " - " << colorClearValue[1] << " = " << (mColorClearValue[1] - colorClearValue[1]) << "\n" << mColorClearValue[2] << " - " << colorClearValue[2] << " = " << (mColorClearValue[2] - colorClearValue[2]) << "\n" << mColorClearValue[3] << " - " << colorClearValue[3] << " = " << (mColorClearValue[3] - colorClearValue[3]); } MOZ_ASSERT(ok); 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(!GetAndFlushUnderlyingGLErrors()); #endif } const char* InfoFrom(WebGLTexImageFunc func, WebGLTexDimensions dims) { switch (dims) { case WebGLTexDimensions::Tex2D: switch (func) { case WebGLTexImageFunc::TexImage: return "texImage2D"; case WebGLTexImageFunc::TexSubImage: return "texSubImage2D"; case WebGLTexImageFunc::CopyTexImage: return "copyTexImage2D"; case WebGLTexImageFunc::CopyTexSubImage: return "copyTexSubImage2D"; case WebGLTexImageFunc::CompTexImage: return "compressedTexImage2D"; case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage2D"; default: MOZ_CRASH("GFX: invalid 2D TexDimensions"); } case WebGLTexDimensions::Tex3D: switch (func) { case WebGLTexImageFunc::TexImage: return "texImage3D"; case WebGLTexImageFunc::TexSubImage: return "texSubImage3D"; case WebGLTexImageFunc::CopyTexSubImage: return "copyTexSubImage3D"; case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage3D"; default: MOZ_CRASH("GFX: invalid 3D TexDimensions"); } default: MOZ_CRASH("GFX: invalid TexDimensions"); } } //// JS::Value StringValue(JSContext* cx, const nsAString& str, ErrorResult& er) { JSString* jsStr = JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length()); if (!jsStr) { er.Throw(NS_ERROR_OUT_OF_MEMORY); return JS::NullValue(); } return JS::StringValue(jsStr); } } // namespace mozilla