1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "WebGLContext.h"
10 #include "GLContext.h"
12 #include "mozilla/Preferences.h"
13 #include "nsIDOMDataContainerEvent.h"
14 #include "nsIDOMEvent.h"
15 #include "nsIScriptSecurityManager.h"
16 #include "nsIVariant.h"
17 #include "nsServiceManagerUtils.h"
19 #include "WebGLBuffer.h"
20 #include "WebGLExtensions.h"
21 #include "WebGLFramebuffer.h"
22 #include "WebGLProgram.h"
23 #include "WebGLTexture.h"
24 #include "WebGLVertexArray.h"
25 #include "WebGLContextUtils.h"
27 #include "mozilla/dom/ScriptSettings.h"
34 IsGLDepthFormat(GLenum webGLFormat
)
36 return (webGLFormat
== LOCAL_GL_DEPTH_COMPONENT
||
37 webGLFormat
== LOCAL_GL_DEPTH_COMPONENT16
||
38 webGLFormat
== LOCAL_GL_DEPTH_COMPONENT32
);
42 IsGLDepthStencilFormat(GLenum webGLFormat
)
44 return (webGLFormat
== LOCAL_GL_DEPTH_STENCIL
||
45 webGLFormat
== LOCAL_GL_DEPTH24_STENCIL8
);
49 FormatHasAlpha(GLenum webGLFormat
)
51 return webGLFormat
== LOCAL_GL_RGBA
||
52 webGLFormat
== LOCAL_GL_LUMINANCE_ALPHA
||
53 webGLFormat
== LOCAL_GL_ALPHA
||
54 webGLFormat
== LOCAL_GL_RGBA4
||
55 webGLFormat
== LOCAL_GL_RGB5_A1
||
56 webGLFormat
== LOCAL_GL_SRGB_ALPHA
;
59 GLComponents::GLComponents(GLenum format
)
67 case LOCAL_GL_RGB5_A1
:
68 // Luminance + Alpha can be converted
70 case LOCAL_GL_LUMINANCE_ALPHA
:
71 mComponents
|= Components::Alpha
;
75 // Luminance can be converted to and from RGB
76 case LOCAL_GL_LUMINANCE
:
77 mComponents
|= Components::Red
| Components::Green
| Components::Blue
;
80 mComponents
|= Components::Alpha
;
82 case LOCAL_GL_DEPTH_COMPONENT
:
83 mComponents
|= Components::Depth
;
85 case LOCAL_GL_DEPTH_STENCIL
:
86 mComponents
|= Components::Stencil
;
89 MOZ_ASSERT(false, "Unhandled case - GLComponents");
95 GLComponents::IsSubsetOf(const GLComponents
& other
) const
97 return (mComponents
| other
.mComponents
) == other
.mComponents
;
101 * Convert WebGL/ES format and type into GL format and GL internal
102 * format valid for underlying driver.
105 DriverFormatsFromFormatAndType(GLContext
* gl
, GLenum webGLFormat
, GLenum webGLType
,
106 GLenum
* out_driverInternalFormat
, GLenum
* out_driverFormat
)
108 MOZ_ASSERT(out_driverInternalFormat
, "out_driverInternalFormat can't be nullptr.");
109 MOZ_ASSERT(out_driverFormat
, "out_driverFormat can't be nullptr.");
110 if (!out_driverInternalFormat
|| !out_driverFormat
)
113 // ES2 requires that format == internalformat; floating-point is
114 // indicated purely by the type that's loaded. For desktop GL, we
115 // have to specify a floating point internal format.
117 *out_driverInternalFormat
= webGLFormat
;
118 *out_driverFormat
= webGLFormat
;
123 GLenum format
= webGLFormat
;
124 GLenum internalFormat
= LOCAL_GL_NONE
;
126 if (format
== LOCAL_GL_DEPTH_COMPONENT
) {
127 if (webGLType
== LOCAL_GL_UNSIGNED_SHORT
)
128 internalFormat
= LOCAL_GL_DEPTH_COMPONENT16
;
129 else if (webGLType
== LOCAL_GL_UNSIGNED_INT
)
130 internalFormat
= LOCAL_GL_DEPTH_COMPONENT32
;
131 } else if (format
== LOCAL_GL_DEPTH_STENCIL
) {
132 if (webGLType
== LOCAL_GL_UNSIGNED_INT_24_8_EXT
)
133 internalFormat
= LOCAL_GL_DEPTH24_STENCIL8
;
136 case LOCAL_GL_UNSIGNED_BYTE
:
137 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4
:
138 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1
:
139 case LOCAL_GL_UNSIGNED_SHORT_5_6_5
:
140 internalFormat
= format
;
146 internalFormat
= LOCAL_GL_RGBA32F
;
150 internalFormat
= LOCAL_GL_RGB32F
;
154 internalFormat
= LOCAL_GL_ALPHA32F_ARB
;
157 case LOCAL_GL_LUMINANCE
:
158 internalFormat
= LOCAL_GL_LUMINANCE32F_ARB
;
161 case LOCAL_GL_LUMINANCE_ALPHA
:
162 internalFormat
= LOCAL_GL_LUMINANCE_ALPHA32F_ARB
;
167 case LOCAL_GL_HALF_FLOAT_OES
:
170 internalFormat
= LOCAL_GL_RGBA16F
;
174 internalFormat
= LOCAL_GL_RGB16F
;
178 internalFormat
= LOCAL_GL_ALPHA16F_ARB
;
181 case LOCAL_GL_LUMINANCE
:
182 internalFormat
= LOCAL_GL_LUMINANCE16F_ARB
;
185 case LOCAL_GL_LUMINANCE_ALPHA
:
186 internalFormat
= LOCAL_GL_LUMINANCE_ALPHA16F_ARB
;
195 // Handle ES2 and GL differences when supporting sRGB internal formats. GL ES
196 // requires that format == internalformat, but GL will fail in this case.
198 // format -> internalformat
199 // GL_RGB GL_SRGB_EXT
200 // GL_RGBA GL_SRGB_ALPHA_EXT
203 internalFormat
= format
;
204 format
= LOCAL_GL_RGB
;
207 case LOCAL_GL_SRGB_ALPHA
:
208 internalFormat
= format
;
209 format
= LOCAL_GL_RGBA
;
214 MOZ_ASSERT(format
!= LOCAL_GL_NONE
&& internalFormat
!= LOCAL_GL_NONE
,
215 "Coding mistake -- bad format/type passed?");
217 *out_driverInternalFormat
= internalFormat
;
218 *out_driverFormat
= format
;
222 DriverTypeFromType(GLContext
* gl
, GLenum webGLType
)
227 // convert type for half float if not on GLES2
228 GLenum type
= webGLType
;
229 if (type
== LOCAL_GL_HALF_FLOAT_OES
) {
230 if (gl
->IsSupported(gl::GLFeature::texture_half_float
)) {
231 return LOCAL_GL_HALF_FLOAT
;
233 MOZ_ASSERT(gl
->IsExtensionSupported(gl::GLContext::OES_texture_half_float
));
241 WebGLContext::GenerateWarning(const char *fmt
, ...)
246 GenerateWarning(fmt
, ap
);
252 WebGLContext::GenerateWarning(const char *fmt
, va_list ap
)
254 if (!ShouldGenerateWarnings())
257 mAlreadyGeneratedWarnings
++;
260 PR_vsnprintf(buf
, 1024, fmt
, ap
);
262 // no need to print to stderr, as JS_ReportWarning takes care of this for us.
265 JS_ReportWarning(cx
, "WebGL: %s", buf
);
266 if (!ShouldGenerateWarnings()) {
268 "WebGL: No further warnings will be reported for this WebGL context "
269 "(already reported %d warnings)", mAlreadyGeneratedWarnings
);
274 WebGLContext::ShouldGenerateWarnings() const
276 if (mMaxWarnings
== -1) {
280 return mAlreadyGeneratedWarnings
< mMaxWarnings
;
284 WebGLContext::GetImageSize(GLsizei height
,
287 uint32_t packOrUnpackAlignment
)
289 CheckedUint32 checked_plainRowSize
= CheckedUint32(width
) * pixelSize
;
291 // alignedRowSize = row size rounded up to next multiple of packAlignment
292 CheckedUint32 checked_alignedRowSize
= RoundedToNextMultipleOf(checked_plainRowSize
, packOrUnpackAlignment
);
294 // if height is 0, we don't need any memory to store this; without this check, we'll get an overflow
295 CheckedUint32 checked_neededByteLength
296 = height
<= 0 ? 0 : (height
-1) * checked_alignedRowSize
+ checked_plainRowSize
;
298 return checked_neededByteLength
;
302 WebGLContext::SynthesizeGLError(GLenum err
)
304 /* ES2 section 2.5 "GL Errors" states that implementations can have
305 * multiple 'flags', as errors might be caught in different parts of
306 * a distributed implementation.
307 * We're signing up as a distributed implementation here, with
308 * separate flags for WebGL and the underlying GLContext.
315 WebGLContext::SynthesizeGLError(GLenum err
, const char *fmt
, ...)
319 GenerateWarning(fmt
, va
);
322 return SynthesizeGLError(err
);
326 WebGLContext::ErrorInvalidEnum(const char *fmt
, ...)
330 GenerateWarning(fmt
, va
);
333 return SynthesizeGLError(LOCAL_GL_INVALID_ENUM
);
337 WebGLContext::ErrorInvalidEnumInfo(const char *info
, GLenum enumvalue
)
339 return ErrorInvalidEnum("%s: invalid enum value 0x%x", info
, enumvalue
);
343 WebGLContext::ErrorInvalidOperation(const char *fmt
, ...)
347 GenerateWarning(fmt
, va
);
350 return SynthesizeGLError(LOCAL_GL_INVALID_OPERATION
);
354 WebGLContext::ErrorInvalidValue(const char *fmt
, ...)
358 GenerateWarning(fmt
, va
);
361 return SynthesizeGLError(LOCAL_GL_INVALID_VALUE
);
365 WebGLContext::ErrorInvalidFramebufferOperation(const char *fmt
, ...)
369 GenerateWarning(fmt
, va
);
372 return SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
);
376 WebGLContext::ErrorOutOfMemory(const char *fmt
, ...)
380 GenerateWarning(fmt
, va
);
383 return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY
);
387 WebGLContext::ErrorName(GLenum error
)
390 case LOCAL_GL_INVALID_ENUM
:
391 return "INVALID_ENUM";
392 case LOCAL_GL_INVALID_OPERATION
:
393 return "INVALID_OPERATION";
394 case LOCAL_GL_INVALID_VALUE
:
395 return "INVALID_VALUE";
396 case LOCAL_GL_OUT_OF_MEMORY
:
397 return "OUT_OF_MEMORY";
398 case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION
:
399 return "INVALID_FRAMEBUFFER_OPERATION";
400 case LOCAL_GL_NO_ERROR
:
404 return "[unknown WebGL error!]";
409 WebGLContext::EnumName(GLenum glenum
)
412 #define XX(x) case LOCAL_GL_##x: return #x
415 XX(ATC_RGBA_EXPLICIT_ALPHA
);
416 XX(ATC_RGBA_INTERPOLATED_ALPHA
);
417 XX(COMPRESSED_RGBA_PVRTC_2BPPV1
);
418 XX(COMPRESSED_RGBA_PVRTC_4BPPV1
);
419 XX(COMPRESSED_RGBA_S3TC_DXT1_EXT
);
420 XX(COMPRESSED_RGBA_S3TC_DXT3_EXT
);
421 XX(COMPRESSED_RGBA_S3TC_DXT5_EXT
);
422 XX(COMPRESSED_RGB_PVRTC_2BPPV1
);
423 XX(COMPRESSED_RGB_PVRTC_4BPPV1
);
424 XX(COMPRESSED_RGB_S3TC_DXT1_EXT
);
426 XX(DEPTH_COMPONENT16
);
427 XX(DEPTH_COMPONENT32
);
429 XX(DEPTH24_STENCIL8
);
445 XX(TEXTURE_CUBE_MAP
);
446 XX(TEXTURE_CUBE_MAP_NEGATIVE_X
);
447 XX(TEXTURE_CUBE_MAP_NEGATIVE_Y
);
448 XX(TEXTURE_CUBE_MAP_NEGATIVE_Z
);
449 XX(TEXTURE_CUBE_MAP_POSITIVE_X
);
450 XX(TEXTURE_CUBE_MAP_POSITIVE_Y
);
451 XX(TEXTURE_CUBE_MAP_POSITIVE_Z
);
454 XX(UNSIGNED_INT_24_8
);
456 XX(UNSIGNED_SHORT_4_4_4_4
);
457 XX(UNSIGNED_SHORT_5_5_5_1
);
458 XX(UNSIGNED_SHORT_5_6_5
);
462 return "[Unknown enum name]";
467 WebGLContext::IsTextureFormatCompressed(GLenum format
)
470 case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT
:
471 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
:
472 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
:
473 case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
:
474 case LOCAL_GL_ATC_RGB
:
475 case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA
:
476 case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA
:
477 case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1
:
478 case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1
:
479 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1
:
480 case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1
:
481 case LOCAL_GL_ETC1_RGB8_OES
:
489 WebGLContext::GetAndFlushUnderlyingGLErrors()
491 // Get and clear GL error in ALL cases.
492 GLenum error
= gl
->GetAndClearError();
494 // Only store in mUnderlyingGLError if is hasn't already recorded an
496 if (!mUnderlyingGLError
)
497 mUnderlyingGLError
= error
;
505 IsCacheCorrect(float cached
, float actual
)
508 // GL is allowed to do anything it wants for NaNs, so if we're shadowing
509 // a NaN, then whatever `actual` is might be correct.
513 return cached
== actual
;
517 AssertUintParamCorrect(gl::GLContext
* gl
, GLenum pname
, GLuint shadow
)
520 gl
->GetUIntegerv(pname
, &val
);
522 printf_stderr("Failed 0x%04x shadow: Cached 0x%x/%u, should be 0x%x/%u.\n",
523 pname
, shadow
, shadow
, val
, val
);
524 MOZ_ASSERT(false, "Bad cached value.");
529 AssertUintParamCorrect(gl::GLContext
*, GLenum
, GLuint
)
535 WebGLContext::AssertCachedBindings()
538 MakeContextCurrent();
540 GetAndFlushUnderlyingGLErrors();
542 if (IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object
)) {
543 GLuint bound
= mBoundVertexArray
? mBoundVertexArray
->GLName() : 0;
544 AssertUintParamCorrect(gl
, LOCAL_GL_VERTEX_ARRAY_BINDING
, bound
);
547 // Bound object state
548 GLuint bound
= mBoundFramebuffer
? mBoundFramebuffer
->GLName() : 0;
549 AssertUintParamCorrect(gl
, LOCAL_GL_FRAMEBUFFER_BINDING
, bound
);
551 bound
= mCurrentProgram
? mCurrentProgram
->GLName() : 0;
552 AssertUintParamCorrect(gl
, LOCAL_GL_CURRENT_PROGRAM
, bound
);
555 GLenum activeTexture
= mActiveTexture
+ LOCAL_GL_TEXTURE0
;
556 AssertUintParamCorrect(gl
, LOCAL_GL_ACTIVE_TEXTURE
, activeTexture
);
558 WebGLTexture
* curTex
= activeBoundTextureForTarget(LOCAL_GL_TEXTURE_2D
);
559 bound
= curTex
? curTex
->GLName() : 0;
560 AssertUintParamCorrect(gl
, LOCAL_GL_TEXTURE_BINDING_2D
, bound
);
562 curTex
= activeBoundTextureForTarget(LOCAL_GL_TEXTURE_CUBE_MAP
);
563 bound
= curTex
? curTex
->GLName() : 0;
564 AssertUintParamCorrect(gl
, LOCAL_GL_TEXTURE_BINDING_CUBE_MAP
, bound
);
567 bound
= mBoundArrayBuffer
? mBoundArrayBuffer
->GLName() : 0;
568 AssertUintParamCorrect(gl
, LOCAL_GL_ARRAY_BUFFER_BINDING
, bound
);
570 MOZ_ASSERT(mBoundVertexArray
);
571 WebGLBuffer
* curBuff
= mBoundVertexArray
->mElementArrayBuffer
;
572 bound
= curBuff
? curBuff
->GLName() : 0;
573 AssertUintParamCorrect(gl
, LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING
, bound
);
575 MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
580 WebGLContext::AssertCachedState()
583 MakeContextCurrent();
585 GetAndFlushUnderlyingGLErrors();
588 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers
)) {
589 AssertUintParamCorrect(gl
, LOCAL_GL_MAX_COLOR_ATTACHMENTS
, mGLMaxColorAttachments
);
590 AssertUintParamCorrect(gl
, LOCAL_GL_MAX_DRAW_BUFFERS
, mGLMaxDrawBuffers
);
594 MOZ_ASSERT(gl
->fIsEnabled(LOCAL_GL_SCISSOR_TEST
) == mScissorTestEnabled
);
595 MOZ_ASSERT(gl
->fIsEnabled(LOCAL_GL_DITHER
) == mDitherEnabled
);
596 MOZ_ASSERT_IF(IsWebGL2(),
597 gl
->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD
) == mRasterizerDiscardEnabled
);
600 realGLboolean colorWriteMask
[4] = {0, 0, 0, 0};
601 gl
->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK
, colorWriteMask
);
602 MOZ_ASSERT(colorWriteMask
[0] == mColorWriteMask
[0] &&
603 colorWriteMask
[1] == mColorWriteMask
[1] &&
604 colorWriteMask
[2] == mColorWriteMask
[2] &&
605 colorWriteMask
[3] == mColorWriteMask
[3]);
607 GLfloat colorClearValue
[4] = {0.0f
, 0.0f
, 0.0f
, 0.0f
};
608 gl
->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE
, colorClearValue
);
609 MOZ_ASSERT(IsCacheCorrect(mColorClearValue
[0], colorClearValue
[0]) &&
610 IsCacheCorrect(mColorClearValue
[1], colorClearValue
[1]) &&
611 IsCacheCorrect(mColorClearValue
[2], colorClearValue
[2]) &&
612 IsCacheCorrect(mColorClearValue
[3], colorClearValue
[3]));
614 realGLboolean depthWriteMask
= 0;
615 gl
->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK
, &depthWriteMask
);
616 MOZ_ASSERT(depthWriteMask
== mDepthWriteMask
);
618 GLfloat depthClearValue
= 0.0f
;
619 gl
->fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE
, &depthClearValue
);
620 MOZ_ASSERT(IsCacheCorrect(mDepthClearValue
, depthClearValue
));
622 AssertUintParamCorrect(gl
, LOCAL_GL_STENCIL_CLEAR_VALUE
, mStencilClearValue
);
624 AssertUintParamCorrect(gl
, LOCAL_GL_STENCIL_REF
, mStencilRefFront
);
625 AssertUintParamCorrect(gl
, LOCAL_GL_STENCIL_BACK_REF
, mStencilRefBack
);
627 AssertUintParamCorrect(gl
, LOCAL_GL_STENCIL_VALUE_MASK
, mStencilValueMaskFront
);
628 AssertUintParamCorrect(gl
, LOCAL_GL_STENCIL_BACK_VALUE_MASK
, mStencilValueMaskBack
);
630 AssertUintParamCorrect(gl
, LOCAL_GL_STENCIL_WRITEMASK
, mStencilWriteMaskFront
);
631 AssertUintParamCorrect(gl
, LOCAL_GL_STENCIL_BACK_WRITEMASK
, mStencilWriteMaskBack
);
634 GLint int4
[4] = {0, 0, 0, 0};
635 gl
->fGetIntegerv(LOCAL_GL_VIEWPORT
, int4
);
636 MOZ_ASSERT(int4
[0] == mViewportX
&&
637 int4
[1] == mViewportY
&&
638 int4
[2] == mViewportWidth
&&
639 int4
[3] == mViewportHeight
);
641 AssertUintParamCorrect(gl
, LOCAL_GL_PACK_ALIGNMENT
, mPixelStorePackAlignment
);
642 AssertUintParamCorrect(gl
, LOCAL_GL_UNPACK_ALIGNMENT
, mPixelStoreUnpackAlignment
);
644 MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
648 } // namespace mozilla