michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: michael@0: #include "WebGLContext.h" michael@0: #include "GLContext.h" michael@0: michael@0: #include "prprf.h" michael@0: michael@0: #include "jsapi.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIVariant.h" michael@0: #include "nsCxPusher.h" michael@0: michael@0: #include "nsIDOMEvent.h" michael@0: #include "nsIDOMDataContainerEvent.h" michael@0: michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: namespace mozilla { michael@0: michael@0: using namespace gl; michael@0: michael@0: bool michael@0: IsGLDepthFormat(GLenum webGLFormat) michael@0: { michael@0: return (webGLFormat == LOCAL_GL_DEPTH_COMPONENT || michael@0: webGLFormat == LOCAL_GL_DEPTH_COMPONENT16 || michael@0: webGLFormat == LOCAL_GL_DEPTH_COMPONENT32); michael@0: } michael@0: michael@0: bool michael@0: IsGLDepthStencilFormat(GLenum webGLFormat) michael@0: { michael@0: return (webGLFormat == LOCAL_GL_DEPTH_STENCIL || michael@0: webGLFormat == LOCAL_GL_DEPTH24_STENCIL8); michael@0: } michael@0: michael@0: bool michael@0: FormatHasAlpha(GLenum webGLFormat) michael@0: { michael@0: return webGLFormat == LOCAL_GL_RGBA || michael@0: webGLFormat == LOCAL_GL_LUMINANCE_ALPHA || michael@0: webGLFormat == LOCAL_GL_ALPHA || michael@0: webGLFormat == LOCAL_GL_RGBA4 || michael@0: webGLFormat == LOCAL_GL_RGB5_A1 || michael@0: webGLFormat == LOCAL_GL_SRGB_ALPHA; michael@0: } michael@0: michael@0: /** michael@0: * Convert WebGL/ES format and type into GL format and GL internal michael@0: * format valid for underlying driver. michael@0: */ michael@0: void michael@0: DriverFormatsFromFormatAndType(GLContext* gl, GLenum webGLFormat, GLenum webGLType, michael@0: GLenum* out_driverInternalFormat, GLenum* out_driverFormat) michael@0: { michael@0: MOZ_ASSERT(out_driverInternalFormat, "out_driverInternalFormat can't be nullptr."); michael@0: MOZ_ASSERT(out_driverFormat, "out_driverFormat can't be nullptr."); michael@0: if (!out_driverInternalFormat || !out_driverFormat) michael@0: return; michael@0: michael@0: // ES2 requires that format == internalformat; floating-point is michael@0: // indicated purely by the type that's loaded. For desktop GL, we michael@0: // have to specify a floating point internal format. michael@0: if (gl->IsGLES()) { michael@0: *out_driverInternalFormat = webGLFormat; michael@0: *out_driverFormat = webGLFormat; michael@0: michael@0: return; michael@0: } michael@0: michael@0: GLenum format = webGLFormat; michael@0: GLenum internalFormat = LOCAL_GL_NONE; michael@0: michael@0: if (format == LOCAL_GL_DEPTH_COMPONENT) { michael@0: if (webGLType == LOCAL_GL_UNSIGNED_SHORT) michael@0: internalFormat = LOCAL_GL_DEPTH_COMPONENT16; michael@0: else if (webGLType == LOCAL_GL_UNSIGNED_INT) michael@0: internalFormat = LOCAL_GL_DEPTH_COMPONENT32; michael@0: } else if (format == LOCAL_GL_DEPTH_STENCIL) { michael@0: if (webGLType == LOCAL_GL_UNSIGNED_INT_24_8_EXT) michael@0: internalFormat = LOCAL_GL_DEPTH24_STENCIL8; michael@0: } else { michael@0: switch (webGLType) { michael@0: case LOCAL_GL_UNSIGNED_BYTE: michael@0: case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_6_5: michael@0: internalFormat = format; michael@0: break; michael@0: michael@0: case LOCAL_GL_FLOAT: michael@0: switch (format) { michael@0: case LOCAL_GL_RGBA: michael@0: internalFormat = LOCAL_GL_RGBA32F; michael@0: break; michael@0: michael@0: case LOCAL_GL_RGB: michael@0: internalFormat = LOCAL_GL_RGB32F; michael@0: break; michael@0: michael@0: case LOCAL_GL_ALPHA: michael@0: internalFormat = LOCAL_GL_ALPHA32F_ARB; michael@0: break; michael@0: michael@0: case LOCAL_GL_LUMINANCE: michael@0: internalFormat = LOCAL_GL_LUMINANCE32F_ARB; michael@0: break; michael@0: michael@0: case LOCAL_GL_LUMINANCE_ALPHA: michael@0: internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB; michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: case LOCAL_GL_HALF_FLOAT_OES: michael@0: switch (format) { michael@0: case LOCAL_GL_RGBA: michael@0: internalFormat = LOCAL_GL_RGBA16F; michael@0: break; michael@0: michael@0: case LOCAL_GL_RGB: michael@0: internalFormat = LOCAL_GL_RGB16F; michael@0: break; michael@0: michael@0: case LOCAL_GL_ALPHA: michael@0: internalFormat = LOCAL_GL_ALPHA16F_ARB; michael@0: break; michael@0: michael@0: case LOCAL_GL_LUMINANCE: michael@0: internalFormat = LOCAL_GL_LUMINANCE16F_ARB; michael@0: break; michael@0: michael@0: case LOCAL_GL_LUMINANCE_ALPHA: michael@0: internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB; michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES michael@0: // requires that format == internalformat, but GL will fail in this case. michael@0: // GL requires: michael@0: // format -> internalformat michael@0: // GL_RGB GL_SRGB_EXT michael@0: // GL_RGBA GL_SRGB_ALPHA_EXT michael@0: switch (format) { michael@0: case LOCAL_GL_SRGB: michael@0: internalFormat = format; michael@0: format = LOCAL_GL_RGB; michael@0: break; michael@0: michael@0: case LOCAL_GL_SRGB_ALPHA: michael@0: internalFormat = format; michael@0: format = LOCAL_GL_RGBA; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSERT(format != LOCAL_GL_NONE && internalFormat != LOCAL_GL_NONE, michael@0: "Coding mistake -- bad format/type passed?"); michael@0: michael@0: *out_driverInternalFormat = internalFormat; michael@0: *out_driverFormat = format; michael@0: } michael@0: michael@0: GLenum michael@0: DriverTypeFromType(GLContext* gl, GLenum webGLType) michael@0: { michael@0: if (gl->IsGLES()) michael@0: return webGLType; michael@0: michael@0: // convert type for half float if not on GLES2 michael@0: GLenum type = webGLType; michael@0: if (type == LOCAL_GL_HALF_FLOAT_OES) { michael@0: if (gl->IsSupported(gl::GLFeature::texture_half_float)) { michael@0: return LOCAL_GL_HALF_FLOAT; michael@0: } else { michael@0: MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float)); michael@0: } michael@0: } michael@0: michael@0: return webGLType; michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: void michael@0: WebGLContext::GenerateWarning(const char *fmt, ...) michael@0: { michael@0: va_list ap; michael@0: va_start(ap, fmt); michael@0: michael@0: GenerateWarning(fmt, ap); michael@0: michael@0: va_end(ap); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GenerateWarning(const char *fmt, va_list ap) michael@0: { michael@0: if (!ShouldGenerateWarnings()) michael@0: return; michael@0: michael@0: mAlreadyGeneratedWarnings++; michael@0: michael@0: char buf[1024]; michael@0: PR_vsnprintf(buf, 1024, fmt, ap); michael@0: michael@0: // no need to print to stderr, as JS_ReportWarning takes care of this for us. michael@0: michael@0: AutoJSContext cx; michael@0: JS_ReportWarning(cx, "WebGL: %s", buf); michael@0: if (!ShouldGenerateWarnings()) { michael@0: JS_ReportWarning(cx, michael@0: "WebGL: No further warnings will be reported for this WebGL context " michael@0: "(already reported %d warnings)", mAlreadyGeneratedWarnings); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::ShouldGenerateWarnings() const michael@0: { michael@0: if (mMaxWarnings == -1) { michael@0: return true; michael@0: } michael@0: michael@0: return mAlreadyGeneratedWarnings < mMaxWarnings; michael@0: } michael@0: michael@0: CheckedUint32 michael@0: WebGLContext::GetImageSize(GLsizei height, michael@0: GLsizei width, michael@0: uint32_t pixelSize, michael@0: uint32_t packOrUnpackAlignment) michael@0: { michael@0: CheckedUint32 checked_plainRowSize = CheckedUint32(width) * pixelSize; michael@0: michael@0: // alignedRowSize = row size rounded up to next multiple of packAlignment michael@0: CheckedUint32 checked_alignedRowSize = RoundedToNextMultipleOf(checked_plainRowSize, packOrUnpackAlignment); michael@0: michael@0: // if height is 0, we don't need any memory to store this; without this check, we'll get an overflow michael@0: CheckedUint32 checked_neededByteLength michael@0: = height <= 0 ? 0 : (height-1) * checked_alignedRowSize + checked_plainRowSize; michael@0: michael@0: return checked_neededByteLength; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::SynthesizeGLError(GLenum err) michael@0: { michael@0: /* ES2 section 2.5 "GL Errors" states that implementations can have michael@0: * multiple 'flags', as errors might be caught in different parts of michael@0: * a distributed implementation. michael@0: * We're signing up as a distributed implementation here, with michael@0: * separate flags for WebGL and the underlying GLContext. michael@0: */ michael@0: if (!mWebGLError) michael@0: mWebGLError = err; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::SynthesizeGLError(GLenum err, const char *fmt, ...) michael@0: { michael@0: va_list va; michael@0: va_start(va, fmt); michael@0: GenerateWarning(fmt, va); michael@0: va_end(va); michael@0: michael@0: return SynthesizeGLError(err); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ErrorInvalidEnum(const char *fmt, ...) michael@0: { michael@0: va_list va; michael@0: va_start(va, fmt); michael@0: GenerateWarning(fmt, va); michael@0: va_end(va); michael@0: michael@0: return SynthesizeGLError(LOCAL_GL_INVALID_ENUM); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ErrorInvalidEnumInfo(const char *info, GLenum enumvalue) michael@0: { michael@0: return ErrorInvalidEnum("%s: invalid enum value 0x%x", info, enumvalue); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ErrorInvalidOperation(const char *fmt, ...) michael@0: { michael@0: va_list va; michael@0: va_start(va, fmt); michael@0: GenerateWarning(fmt, va); michael@0: va_end(va); michael@0: michael@0: return SynthesizeGLError(LOCAL_GL_INVALID_OPERATION); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ErrorInvalidValue(const char *fmt, ...) michael@0: { michael@0: va_list va; michael@0: va_start(va, fmt); michael@0: GenerateWarning(fmt, va); michael@0: va_end(va); michael@0: michael@0: return SynthesizeGLError(LOCAL_GL_INVALID_VALUE); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ErrorInvalidFramebufferOperation(const char *fmt, ...) michael@0: { michael@0: va_list va; michael@0: va_start(va, fmt); michael@0: GenerateWarning(fmt, va); michael@0: va_end(va); michael@0: michael@0: return SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ErrorOutOfMemory(const char *fmt, ...) michael@0: { michael@0: va_list va; michael@0: va_start(va, fmt); michael@0: GenerateWarning(fmt, va); michael@0: va_end(va); michael@0: michael@0: return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: const char * michael@0: WebGLContext::ErrorName(GLenum error) michael@0: { michael@0: switch(error) { michael@0: case LOCAL_GL_INVALID_ENUM: michael@0: return "INVALID_ENUM"; michael@0: case LOCAL_GL_INVALID_OPERATION: michael@0: return "INVALID_OPERATION"; michael@0: case LOCAL_GL_INVALID_VALUE: michael@0: return "INVALID_VALUE"; michael@0: case LOCAL_GL_OUT_OF_MEMORY: michael@0: return "OUT_OF_MEMORY"; michael@0: case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION: michael@0: return "INVALID_FRAMEBUFFER_OPERATION"; michael@0: case LOCAL_GL_NO_ERROR: michael@0: return "NO_ERROR"; michael@0: default: michael@0: MOZ_ASSERT(false); michael@0: return "[unknown WebGL error!]"; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::IsTextureFormatCompressed(GLenum format) michael@0: { michael@0: switch (format) { michael@0: case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: michael@0: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: michael@0: case LOCAL_GL_ATC_RGB: michael@0: case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: michael@0: case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: michael@0: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: michael@0: case LOCAL_GL_ETC1_RGB8_OES: michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: GLenum michael@0: WebGLContext::GetAndFlushUnderlyingGLErrors() michael@0: { michael@0: // Get and clear GL error in ALL cases. michael@0: GLenum error = gl->GetAndClearError(); michael@0: michael@0: // Only store in mUnderlyingGLError if is hasn't already recorded an michael@0: // error. michael@0: if (!mUnderlyingGLError) michael@0: mUnderlyingGLError = error; michael@0: michael@0: return error; michael@0: }