1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/src/WebGLContextUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,400 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <stdarg.h> 1.10 + 1.11 +#include "WebGLContext.h" 1.12 +#include "GLContext.h" 1.13 + 1.14 +#include "prprf.h" 1.15 + 1.16 +#include "jsapi.h" 1.17 +#include "nsIScriptSecurityManager.h" 1.18 +#include "nsServiceManagerUtils.h" 1.19 +#include "nsIVariant.h" 1.20 +#include "nsCxPusher.h" 1.21 + 1.22 +#include "nsIDOMEvent.h" 1.23 +#include "nsIDOMDataContainerEvent.h" 1.24 + 1.25 +#include "mozilla/Preferences.h" 1.26 + 1.27 +using namespace mozilla; 1.28 + 1.29 +namespace mozilla { 1.30 + 1.31 +using namespace gl; 1.32 + 1.33 +bool 1.34 +IsGLDepthFormat(GLenum webGLFormat) 1.35 +{ 1.36 + return (webGLFormat == LOCAL_GL_DEPTH_COMPONENT || 1.37 + webGLFormat == LOCAL_GL_DEPTH_COMPONENT16 || 1.38 + webGLFormat == LOCAL_GL_DEPTH_COMPONENT32); 1.39 +} 1.40 + 1.41 +bool 1.42 +IsGLDepthStencilFormat(GLenum webGLFormat) 1.43 +{ 1.44 + return (webGLFormat == LOCAL_GL_DEPTH_STENCIL || 1.45 + webGLFormat == LOCAL_GL_DEPTH24_STENCIL8); 1.46 +} 1.47 + 1.48 +bool 1.49 +FormatHasAlpha(GLenum webGLFormat) 1.50 +{ 1.51 + return webGLFormat == LOCAL_GL_RGBA || 1.52 + webGLFormat == LOCAL_GL_LUMINANCE_ALPHA || 1.53 + webGLFormat == LOCAL_GL_ALPHA || 1.54 + webGLFormat == LOCAL_GL_RGBA4 || 1.55 + webGLFormat == LOCAL_GL_RGB5_A1 || 1.56 + webGLFormat == LOCAL_GL_SRGB_ALPHA; 1.57 +} 1.58 + 1.59 +/** 1.60 + * Convert WebGL/ES format and type into GL format and GL internal 1.61 + * format valid for underlying driver. 1.62 + */ 1.63 +void 1.64 +DriverFormatsFromFormatAndType(GLContext* gl, GLenum webGLFormat, GLenum webGLType, 1.65 + GLenum* out_driverInternalFormat, GLenum* out_driverFormat) 1.66 +{ 1.67 + MOZ_ASSERT(out_driverInternalFormat, "out_driverInternalFormat can't be nullptr."); 1.68 + MOZ_ASSERT(out_driverFormat, "out_driverFormat can't be nullptr."); 1.69 + if (!out_driverInternalFormat || !out_driverFormat) 1.70 + return; 1.71 + 1.72 + // ES2 requires that format == internalformat; floating-point is 1.73 + // indicated purely by the type that's loaded. For desktop GL, we 1.74 + // have to specify a floating point internal format. 1.75 + if (gl->IsGLES()) { 1.76 + *out_driverInternalFormat = webGLFormat; 1.77 + *out_driverFormat = webGLFormat; 1.78 + 1.79 + return; 1.80 + } 1.81 + 1.82 + GLenum format = webGLFormat; 1.83 + GLenum internalFormat = LOCAL_GL_NONE; 1.84 + 1.85 + if (format == LOCAL_GL_DEPTH_COMPONENT) { 1.86 + if (webGLType == LOCAL_GL_UNSIGNED_SHORT) 1.87 + internalFormat = LOCAL_GL_DEPTH_COMPONENT16; 1.88 + else if (webGLType == LOCAL_GL_UNSIGNED_INT) 1.89 + internalFormat = LOCAL_GL_DEPTH_COMPONENT32; 1.90 + } else if (format == LOCAL_GL_DEPTH_STENCIL) { 1.91 + if (webGLType == LOCAL_GL_UNSIGNED_INT_24_8_EXT) 1.92 + internalFormat = LOCAL_GL_DEPTH24_STENCIL8; 1.93 + } else { 1.94 + switch (webGLType) { 1.95 + case LOCAL_GL_UNSIGNED_BYTE: 1.96 + case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: 1.97 + case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: 1.98 + case LOCAL_GL_UNSIGNED_SHORT_5_6_5: 1.99 + internalFormat = format; 1.100 + break; 1.101 + 1.102 + case LOCAL_GL_FLOAT: 1.103 + switch (format) { 1.104 + case LOCAL_GL_RGBA: 1.105 + internalFormat = LOCAL_GL_RGBA32F; 1.106 + break; 1.107 + 1.108 + case LOCAL_GL_RGB: 1.109 + internalFormat = LOCAL_GL_RGB32F; 1.110 + break; 1.111 + 1.112 + case LOCAL_GL_ALPHA: 1.113 + internalFormat = LOCAL_GL_ALPHA32F_ARB; 1.114 + break; 1.115 + 1.116 + case LOCAL_GL_LUMINANCE: 1.117 + internalFormat = LOCAL_GL_LUMINANCE32F_ARB; 1.118 + break; 1.119 + 1.120 + case LOCAL_GL_LUMINANCE_ALPHA: 1.121 + internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB; 1.122 + break; 1.123 + } 1.124 + break; 1.125 + 1.126 + case LOCAL_GL_HALF_FLOAT_OES: 1.127 + switch (format) { 1.128 + case LOCAL_GL_RGBA: 1.129 + internalFormat = LOCAL_GL_RGBA16F; 1.130 + break; 1.131 + 1.132 + case LOCAL_GL_RGB: 1.133 + internalFormat = LOCAL_GL_RGB16F; 1.134 + break; 1.135 + 1.136 + case LOCAL_GL_ALPHA: 1.137 + internalFormat = LOCAL_GL_ALPHA16F_ARB; 1.138 + break; 1.139 + 1.140 + case LOCAL_GL_LUMINANCE: 1.141 + internalFormat = LOCAL_GL_LUMINANCE16F_ARB; 1.142 + break; 1.143 + 1.144 + case LOCAL_GL_LUMINANCE_ALPHA: 1.145 + internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB; 1.146 + break; 1.147 + } 1.148 + break; 1.149 + 1.150 + default: 1.151 + break; 1.152 + } 1.153 + 1.154 + // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES 1.155 + // requires that format == internalformat, but GL will fail in this case. 1.156 + // GL requires: 1.157 + // format -> internalformat 1.158 + // GL_RGB GL_SRGB_EXT 1.159 + // GL_RGBA GL_SRGB_ALPHA_EXT 1.160 + switch (format) { 1.161 + case LOCAL_GL_SRGB: 1.162 + internalFormat = format; 1.163 + format = LOCAL_GL_RGB; 1.164 + break; 1.165 + 1.166 + case LOCAL_GL_SRGB_ALPHA: 1.167 + internalFormat = format; 1.168 + format = LOCAL_GL_RGBA; 1.169 + break; 1.170 + } 1.171 + } 1.172 + 1.173 + MOZ_ASSERT(format != LOCAL_GL_NONE && internalFormat != LOCAL_GL_NONE, 1.174 + "Coding mistake -- bad format/type passed?"); 1.175 + 1.176 + *out_driverInternalFormat = internalFormat; 1.177 + *out_driverFormat = format; 1.178 +} 1.179 + 1.180 +GLenum 1.181 +DriverTypeFromType(GLContext* gl, GLenum webGLType) 1.182 +{ 1.183 + if (gl->IsGLES()) 1.184 + return webGLType; 1.185 + 1.186 + // convert type for half float if not on GLES2 1.187 + GLenum type = webGLType; 1.188 + if (type == LOCAL_GL_HALF_FLOAT_OES) { 1.189 + if (gl->IsSupported(gl::GLFeature::texture_half_float)) { 1.190 + return LOCAL_GL_HALF_FLOAT; 1.191 + } else { 1.192 + MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float)); 1.193 + } 1.194 + } 1.195 + 1.196 + return webGLType; 1.197 +} 1.198 + 1.199 +} // namespace mozilla 1.200 + 1.201 +void 1.202 +WebGLContext::GenerateWarning(const char *fmt, ...) 1.203 +{ 1.204 + va_list ap; 1.205 + va_start(ap, fmt); 1.206 + 1.207 + GenerateWarning(fmt, ap); 1.208 + 1.209 + va_end(ap); 1.210 +} 1.211 + 1.212 +void 1.213 +WebGLContext::GenerateWarning(const char *fmt, va_list ap) 1.214 +{ 1.215 + if (!ShouldGenerateWarnings()) 1.216 + return; 1.217 + 1.218 + mAlreadyGeneratedWarnings++; 1.219 + 1.220 + char buf[1024]; 1.221 + PR_vsnprintf(buf, 1024, fmt, ap); 1.222 + 1.223 + // no need to print to stderr, as JS_ReportWarning takes care of this for us. 1.224 + 1.225 + AutoJSContext cx; 1.226 + JS_ReportWarning(cx, "WebGL: %s", buf); 1.227 + if (!ShouldGenerateWarnings()) { 1.228 + JS_ReportWarning(cx, 1.229 + "WebGL: No further warnings will be reported for this WebGL context " 1.230 + "(already reported %d warnings)", mAlreadyGeneratedWarnings); 1.231 + } 1.232 +} 1.233 + 1.234 +bool 1.235 +WebGLContext::ShouldGenerateWarnings() const 1.236 +{ 1.237 + if (mMaxWarnings == -1) { 1.238 + return true; 1.239 + } 1.240 + 1.241 + return mAlreadyGeneratedWarnings < mMaxWarnings; 1.242 +} 1.243 + 1.244 +CheckedUint32 1.245 +WebGLContext::GetImageSize(GLsizei height, 1.246 + GLsizei width, 1.247 + uint32_t pixelSize, 1.248 + uint32_t packOrUnpackAlignment) 1.249 +{ 1.250 + CheckedUint32 checked_plainRowSize = CheckedUint32(width) * pixelSize; 1.251 + 1.252 + // alignedRowSize = row size rounded up to next multiple of packAlignment 1.253 + CheckedUint32 checked_alignedRowSize = RoundedToNextMultipleOf(checked_plainRowSize, packOrUnpackAlignment); 1.254 + 1.255 + // if height is 0, we don't need any memory to store this; without this check, we'll get an overflow 1.256 + CheckedUint32 checked_neededByteLength 1.257 + = height <= 0 ? 0 : (height-1) * checked_alignedRowSize + checked_plainRowSize; 1.258 + 1.259 + return checked_neededByteLength; 1.260 +} 1.261 + 1.262 +void 1.263 +WebGLContext::SynthesizeGLError(GLenum err) 1.264 +{ 1.265 + /* ES2 section 2.5 "GL Errors" states that implementations can have 1.266 + * multiple 'flags', as errors might be caught in different parts of 1.267 + * a distributed implementation. 1.268 + * We're signing up as a distributed implementation here, with 1.269 + * separate flags for WebGL and the underlying GLContext. 1.270 + */ 1.271 + if (!mWebGLError) 1.272 + mWebGLError = err; 1.273 +} 1.274 + 1.275 +void 1.276 +WebGLContext::SynthesizeGLError(GLenum err, const char *fmt, ...) 1.277 +{ 1.278 + va_list va; 1.279 + va_start(va, fmt); 1.280 + GenerateWarning(fmt, va); 1.281 + va_end(va); 1.282 + 1.283 + return SynthesizeGLError(err); 1.284 +} 1.285 + 1.286 +void 1.287 +WebGLContext::ErrorInvalidEnum(const char *fmt, ...) 1.288 +{ 1.289 + va_list va; 1.290 + va_start(va, fmt); 1.291 + GenerateWarning(fmt, va); 1.292 + va_end(va); 1.293 + 1.294 + return SynthesizeGLError(LOCAL_GL_INVALID_ENUM); 1.295 +} 1.296 + 1.297 +void 1.298 +WebGLContext::ErrorInvalidEnumInfo(const char *info, GLenum enumvalue) 1.299 +{ 1.300 + return ErrorInvalidEnum("%s: invalid enum value 0x%x", info, enumvalue); 1.301 +} 1.302 + 1.303 +void 1.304 +WebGLContext::ErrorInvalidOperation(const char *fmt, ...) 1.305 +{ 1.306 + va_list va; 1.307 + va_start(va, fmt); 1.308 + GenerateWarning(fmt, va); 1.309 + va_end(va); 1.310 + 1.311 + return SynthesizeGLError(LOCAL_GL_INVALID_OPERATION); 1.312 +} 1.313 + 1.314 +void 1.315 +WebGLContext::ErrorInvalidValue(const char *fmt, ...) 1.316 +{ 1.317 + va_list va; 1.318 + va_start(va, fmt); 1.319 + GenerateWarning(fmt, va); 1.320 + va_end(va); 1.321 + 1.322 + return SynthesizeGLError(LOCAL_GL_INVALID_VALUE); 1.323 +} 1.324 + 1.325 +void 1.326 +WebGLContext::ErrorInvalidFramebufferOperation(const char *fmt, ...) 1.327 +{ 1.328 + va_list va; 1.329 + va_start(va, fmt); 1.330 + GenerateWarning(fmt, va); 1.331 + va_end(va); 1.332 + 1.333 + return SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); 1.334 +} 1.335 + 1.336 +void 1.337 +WebGLContext::ErrorOutOfMemory(const char *fmt, ...) 1.338 +{ 1.339 + va_list va; 1.340 + va_start(va, fmt); 1.341 + GenerateWarning(fmt, va); 1.342 + va_end(va); 1.343 + 1.344 + return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY); 1.345 +} 1.346 + 1.347 +const char * 1.348 +WebGLContext::ErrorName(GLenum error) 1.349 +{ 1.350 + switch(error) { 1.351 + case LOCAL_GL_INVALID_ENUM: 1.352 + return "INVALID_ENUM"; 1.353 + case LOCAL_GL_INVALID_OPERATION: 1.354 + return "INVALID_OPERATION"; 1.355 + case LOCAL_GL_INVALID_VALUE: 1.356 + return "INVALID_VALUE"; 1.357 + case LOCAL_GL_OUT_OF_MEMORY: 1.358 + return "OUT_OF_MEMORY"; 1.359 + case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION: 1.360 + return "INVALID_FRAMEBUFFER_OPERATION"; 1.361 + case LOCAL_GL_NO_ERROR: 1.362 + return "NO_ERROR"; 1.363 + default: 1.364 + MOZ_ASSERT(false); 1.365 + return "[unknown WebGL error!]"; 1.366 + } 1.367 +} 1.368 + 1.369 +bool 1.370 +WebGLContext::IsTextureFormatCompressed(GLenum format) 1.371 +{ 1.372 + switch (format) { 1.373 + case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: 1.374 + case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: 1.375 + case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: 1.376 + case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: 1.377 + case LOCAL_GL_ATC_RGB: 1.378 + case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: 1.379 + case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: 1.380 + case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: 1.381 + case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: 1.382 + case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: 1.383 + case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: 1.384 + case LOCAL_GL_ETC1_RGB8_OES: 1.385 + return true; 1.386 + default: 1.387 + return false; 1.388 + } 1.389 +} 1.390 + 1.391 +GLenum 1.392 +WebGLContext::GetAndFlushUnderlyingGLErrors() 1.393 +{ 1.394 + // Get and clear GL error in ALL cases. 1.395 + GLenum error = gl->GetAndClearError(); 1.396 + 1.397 + // Only store in mUnderlyingGLError if is hasn't already recorded an 1.398 + // error. 1.399 + if (!mUnderlyingGLError) 1.400 + mUnderlyingGLError = error; 1.401 + 1.402 + return error; 1.403 +}