Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / canvas / WebGLContextGL.cpp
blob20d003ac49b44e0b640b7ef68ad60c4fc60f44dc
1 /* -*- Mode: C++; tab-width: 4; 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"
7 #include "WebGLContextUtils.h"
8 #include "WebGLBuffer.h"
9 #include "WebGLVertexAttribData.h"
10 #include "WebGLShader.h"
11 #include "WebGLProgram.h"
12 #include "WebGLUniformLocation.h"
13 #include "WebGLFramebuffer.h"
14 #include "WebGLRenderbuffer.h"
15 #include "WebGLShaderPrecisionFormat.h"
16 #include "WebGLTexture.h"
17 #include "WebGLExtensions.h"
18 #include "WebGLVertexArray.h"
20 #include "nsString.h"
21 #include "nsDebug.h"
22 #include "nsReadableUtils.h"
24 #include "gfxContext.h"
25 #include "gfxPlatform.h"
26 #include "GLContext.h"
28 #include "nsContentUtils.h"
29 #include "nsError.h"
30 #include "nsLayoutUtils.h"
32 #include "CanvasUtils.h"
33 #include "gfxUtils.h"
35 #include "jsfriendapi.h"
37 #include "WebGLTexelConversions.h"
38 #include "WebGLValidateStrings.h"
39 #include <algorithm>
41 // needed to check if current OS is lower than 10.7
42 #if defined(MOZ_WIDGET_COCOA)
43 #include "nsCocoaFeatures.h"
44 #endif
46 #include "mozilla/DebugOnly.h"
47 #include "mozilla/dom/BindingUtils.h"
48 #include "mozilla/dom/ImageData.h"
49 #include "mozilla/dom/ToJSValue.h"
50 #include "mozilla/Endian.h"
51 #include "mozilla/fallible.h"
53 using namespace mozilla;
54 using namespace mozilla::dom;
55 using namespace mozilla::gl;
56 using namespace mozilla::gfx;
58 static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize);
60 const WebGLRectangleObject*
61 WebGLContext::CurValidFBRectObject() const
63 const WebGLRectangleObject* rect = nullptr;
65 if (mBoundFramebuffer) {
66 // We don't really need to ask the driver.
67 // Use 'precheck' to just check that our internal state looks good.
68 GLenum precheckStatus = mBoundFramebuffer->PrecheckFramebufferStatus();
69 if (precheckStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
70 rect = &mBoundFramebuffer->RectangleObject();
71 } else {
72 rect = static_cast<const WebGLRectangleObject*>(this);
75 return rect;
79 // WebGL API
82 void
83 WebGLContext::ActiveTexture(GLenum texture)
85 if (IsContextLost())
86 return;
88 if (texture < LOCAL_GL_TEXTURE0 ||
89 texture >= LOCAL_GL_TEXTURE0 + uint32_t(mGLMaxTextureUnits))
91 return ErrorInvalidEnum(
92 "ActiveTexture: texture unit %d out of range. "
93 "Accepted values range from TEXTURE0 to TEXTURE0 + %d. "
94 "Notice that TEXTURE0 != 0.",
95 texture, mGLMaxTextureUnits);
98 MakeContextCurrent();
99 mActiveTexture = texture - LOCAL_GL_TEXTURE0;
100 gl->fActiveTexture(texture);
103 void
104 WebGLContext::AttachShader(WebGLProgram *program, WebGLShader *shader)
106 if (IsContextLost())
107 return;
109 if (!ValidateObject("attachShader: program", program) ||
110 !ValidateObject("attachShader: shader", shader))
111 return;
113 // Per GLSL ES 2.0, we can only have one of each type of shader
114 // attached. This renders the next test somewhat moot, but we'll
115 // leave it for when we support more than one shader of each type.
116 if (program->HasAttachedShaderOfType(shader->ShaderType()))
117 return ErrorInvalidOperation("attachShader: only one of each type of shader may be attached to a program");
119 if (!program->AttachShader(shader))
120 return ErrorInvalidOperation("attachShader: shader is already attached");
124 void
125 WebGLContext::BindAttribLocation(WebGLProgram *prog, GLuint location,
126 const nsAString& name)
128 if (IsContextLost())
129 return;
131 if (!ValidateObject("bindAttribLocation: program", prog))
132 return;
134 GLuint progname = prog->GLName();
136 if (!ValidateGLSLVariableName(name, "bindAttribLocation"))
137 return;
139 if (!ValidateAttribIndex(location, "bindAttribLocation"))
140 return;
142 if (StringBeginsWith(name, NS_LITERAL_STRING("gl_")))
143 return ErrorInvalidOperation("bindAttribLocation: can't set the location of a name that starts with 'gl_'");
145 NS_LossyConvertUTF16toASCII cname(name);
146 nsCString mappedName;
147 if (mShaderValidation) {
148 WebGLProgram::HashMapIdentifier(cname, &mappedName);
149 } else {
150 mappedName.Assign(cname);
153 MakeContextCurrent();
154 gl->fBindAttribLocation(progname, location, mappedName.get());
157 void
158 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer *wfb)
160 if (IsContextLost())
161 return;
163 if (target != LOCAL_GL_FRAMEBUFFER)
164 return ErrorInvalidEnum("bindFramebuffer: target must be GL_FRAMEBUFFER");
166 if (!ValidateObjectAllowDeletedOrNull("bindFramebuffer", wfb))
167 return;
169 // silently ignore a deleted frame buffer
170 if (wfb && wfb->IsDeleted())
171 return;
173 MakeContextCurrent();
175 if (!wfb) {
176 gl->fBindFramebuffer(target, 0);
177 } else {
178 wfb->BindTo(target);
179 GLuint framebuffername = wfb->GLName();
180 gl->fBindFramebuffer(target, framebuffername);
183 mBoundFramebuffer = wfb;
186 void
187 WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer *wrb)
189 if (IsContextLost())
190 return;
192 if (target != LOCAL_GL_RENDERBUFFER)
193 return ErrorInvalidEnumInfo("bindRenderbuffer: target", target);
195 if (!ValidateObjectAllowDeletedOrNull("bindRenderbuffer", wrb))
196 return;
198 // silently ignore a deleted buffer
199 if (wrb && wrb->IsDeleted())
200 return;
202 if (wrb)
203 wrb->BindTo(target);
205 MakeContextCurrent();
207 // Sometimes we emulate renderbuffers (depth-stencil emu), so there's not
208 // always a 1-1 mapping from `wrb` to GL name. Just have `wrb` handle it.
209 if (wrb) {
210 wrb->BindRenderbuffer();
211 } else {
212 gl->fBindRenderbuffer(target, 0);
215 mBoundRenderbuffer = wrb;
218 void
219 WebGLContext::BindTexture(GLenum target, WebGLTexture *newTex)
221 if (IsContextLost())
222 return;
224 if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex))
225 return;
227 // silently ignore a deleted texture
228 if (newTex && newTex->IsDeleted())
229 return;
231 WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
233 if (target == LOCAL_GL_TEXTURE_2D) {
234 currentTexPtr = &mBound2DTextures[mActiveTexture];
235 } else if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
236 currentTexPtr = &mBoundCubeMapTextures[mActiveTexture];
237 } else {
238 return ErrorInvalidEnumInfo("bindTexture: target", target);
241 WebGLTextureFakeBlackStatus currentTexFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
242 if (*currentTexPtr) {
243 currentTexFakeBlackStatus = (*currentTexPtr)->ResolvedFakeBlackStatus();
245 WebGLTextureFakeBlackStatus newTexFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
246 if (newTex) {
247 newTexFakeBlackStatus = newTex->ResolvedFakeBlackStatus();
250 *currentTexPtr = newTex;
252 if (currentTexFakeBlackStatus != newTexFakeBlackStatus) {
253 SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown);
256 MakeContextCurrent();
258 if (newTex)
259 newTex->Bind(target);
260 else
261 gl->fBindTexture(target, 0 /* == texturename */);
264 void WebGLContext::BlendEquation(GLenum mode)
266 if (IsContextLost())
267 return;
269 if (!ValidateBlendEquationEnum(mode, "blendEquation: mode"))
270 return;
272 MakeContextCurrent();
273 gl->fBlendEquation(mode);
276 void WebGLContext::BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
278 if (IsContextLost())
279 return;
281 if (!ValidateBlendEquationEnum(modeRGB, "blendEquationSeparate: modeRGB") ||
282 !ValidateBlendEquationEnum(modeAlpha, "blendEquationSeparate: modeAlpha"))
283 return;
285 MakeContextCurrent();
286 gl->fBlendEquationSeparate(modeRGB, modeAlpha);
289 void WebGLContext::BlendFunc(GLenum sfactor, GLenum dfactor)
291 if (IsContextLost())
292 return;
294 if (!ValidateBlendFuncSrcEnum(sfactor, "blendFunc: sfactor") ||
295 !ValidateBlendFuncDstEnum(dfactor, "blendFunc: dfactor"))
296 return;
298 if (!ValidateBlendFuncEnumsCompatibility(sfactor, dfactor, "blendFuncSeparate: srcRGB and dstRGB"))
299 return;
301 MakeContextCurrent();
302 gl->fBlendFunc(sfactor, dfactor);
305 void
306 WebGLContext::BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
307 GLenum srcAlpha, GLenum dstAlpha)
309 if (IsContextLost())
310 return;
312 if (!ValidateBlendFuncSrcEnum(srcRGB, "blendFuncSeparate: srcRGB") ||
313 !ValidateBlendFuncSrcEnum(srcAlpha, "blendFuncSeparate: srcAlpha") ||
314 !ValidateBlendFuncDstEnum(dstRGB, "blendFuncSeparate: dstRGB") ||
315 !ValidateBlendFuncDstEnum(dstAlpha, "blendFuncSeparate: dstAlpha"))
316 return;
318 // note that we only check compatibity for the RGB enums, no need to for the Alpha enums, see
319 // "Section 6.8 forgetting to mention alpha factors?" thread on the public_webgl mailing list
320 if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "blendFuncSeparate: srcRGB and dstRGB"))
321 return;
323 MakeContextCurrent();
324 gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
327 GLenum
328 WebGLContext::CheckFramebufferStatus(GLenum target)
330 if (IsContextLost())
331 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
333 if (target != LOCAL_GL_FRAMEBUFFER) {
334 ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER");
335 return 0;
338 if (!mBoundFramebuffer)
339 return LOCAL_GL_FRAMEBUFFER_COMPLETE;
341 return mBoundFramebuffer->CheckFramebufferStatus();
344 void
345 WebGLContext::CopyTexSubImage2D_base(GLenum target,
346 GLint level,
347 GLenum internalformat,
348 GLint xoffset,
349 GLint yoffset,
350 GLint x,
351 GLint y,
352 GLsizei width,
353 GLsizei height,
354 bool sub)
356 const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
357 GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
358 GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
360 const char* info = sub ? "copyTexSubImage2D" : "copyTexImage2D";
361 WebGLTexImageFunc func = sub ? WebGLTexImageFunc::CopyTexSubImage : WebGLTexImageFunc::CopyTexImage;
363 // TODO: This changes with color_buffer_float. Reassess when the
364 // patch lands.
365 if (!ValidateTexImage(2, target, level, internalformat,
366 xoffset, yoffset, 0,
367 width, height, 0,
368 0, internalformat, LOCAL_GL_UNSIGNED_BYTE,
369 func))
371 return;
374 if (!ValidateCopyTexImage(internalformat, func))
375 return;
377 if (!mBoundFramebuffer)
378 ClearBackbufferIfNeeded();
380 MakeContextCurrent();
382 WebGLTexture *tex = activeBoundTextureForTarget(target);
384 if (!tex)
385 return ErrorInvalidOperation("%s: no texture is bound to this target");
387 if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
388 if (sub)
389 gl->fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
390 else
391 gl->fCopyTexImage2D(target, level, internalformat, x, y, width, height, 0);
392 } else {
394 // the rect doesn't fit in the framebuffer
396 /*** first, we initialize the texture as black ***/
398 // first, compute the size of the buffer we should allocate to initialize the texture as black
400 if (!ValidateTexInputData(LOCAL_GL_UNSIGNED_BYTE, -1, func))
401 return;
403 uint32_t texelSize = GetBitsPerTexel(internalformat, LOCAL_GL_UNSIGNED_BYTE) / 8;
405 CheckedUint32 checked_neededByteLength =
406 GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment);
408 if (!checked_neededByteLength.isValid())
409 return ErrorInvalidOperation("%s: integer overflow computing the needed buffer size", info);
411 uint32_t bytesNeeded = checked_neededByteLength.value();
413 // now that the size is known, create the buffer
415 // We need some zero pages, because GL doesn't guarantee the
416 // contents of a texture allocated with nullptr data.
417 // Hopefully calloc will just mmap zero pages here.
418 void* tempZeroData = calloc(1, bytesNeeded);
419 if (!tempZeroData)
420 return ErrorOutOfMemory("%s: could not allocate %d bytes (for zero fill)", info, bytesNeeded);
422 // now initialize the texture as black
424 if (sub)
425 gl->fTexSubImage2D(target, level, 0, 0, width, height,
426 internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData);
427 else
428 gl->fTexImage2D(target, level, internalformat, width, height,
429 0, internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData);
430 free(tempZeroData);
432 // if we are completely outside of the framebuffer, we can exit now with our black texture
433 if ( x >= framebufferWidth
434 || x+width <= 0
435 || y >= framebufferHeight
436 || y+height <= 0)
438 // we are completely outside of range, can exit now with buffer filled with zeros
439 return DummyFramebufferOperation(info);
442 GLint actual_x = clamped(x, 0, framebufferWidth);
443 GLint actual_x_plus_width = clamped(x + width, 0, framebufferWidth);
444 GLsizei actual_width = actual_x_plus_width - actual_x;
445 GLint actual_xoffset = xoffset + actual_x - x;
447 GLint actual_y = clamped(y, 0, framebufferHeight);
448 GLint actual_y_plus_height = clamped(y + height, 0, framebufferHeight);
449 GLsizei actual_height = actual_y_plus_height - actual_y;
450 GLint actual_yoffset = yoffset + actual_y - y;
452 gl->fCopyTexSubImage2D(target, level, actual_xoffset, actual_yoffset, actual_x, actual_y, actual_width, actual_height);
456 void
457 WebGLContext::CopyTexImage2D(GLenum target,
458 GLint level,
459 GLenum internalformat,
460 GLint x,
461 GLint y,
462 GLsizei width,
463 GLsizei height,
464 GLint border)
466 if (IsContextLost())
467 return;
469 // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
470 const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage;
471 const GLenum format = internalformat; // WebGL/ES Format
472 const GLenum type = LOCAL_GL_UNSIGNED_BYTE; // WebGL/ES Format
474 if (!ValidateTexImage(2, target, level, format,
475 0, 0, 0,
476 width, height, 0,
477 border, format, type,
478 func))
480 return;
483 if (!ValidateCopyTexImage(format, func))
484 return;
486 if (!mBoundFramebuffer)
487 ClearBackbufferIfNeeded();
489 // check if the memory size of this texture may change with this call
490 bool sizeMayChange = true;
491 WebGLTexture* tex = activeBoundTextureForTarget(target);
492 if (tex->HasImageInfoAt(target, level)) {
493 const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
495 sizeMayChange = width != imageInfo.Width() ||
496 height != imageInfo.Height() ||
497 format != imageInfo.WebGLFormat() ||
498 type != imageInfo.WebGLType();
501 if (sizeMayChange)
502 GetAndFlushUnderlyingGLErrors();
504 CopyTexSubImage2D_base(target, level, format, 0, 0, x, y, width, height, false);
506 if (sizeMayChange) {
507 GLenum error = GetAndFlushUnderlyingGLErrors();
508 if (error) {
509 GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
510 return;
514 tex->SetImageInfo(target, level, width, height, format, type,
515 WebGLImageDataStatus::InitializedImageData);
518 void
519 WebGLContext::CopyTexSubImage2D(GLenum target,
520 GLint level,
521 GLint xoffset,
522 GLint yoffset,
523 GLint x,
524 GLint y,
525 GLsizei width,
526 GLsizei height)
528 if (IsContextLost())
529 return;
531 switch (target) {
532 case LOCAL_GL_TEXTURE_2D:
533 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
534 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
535 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
536 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
537 case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
538 case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
539 break;
540 default:
541 return ErrorInvalidEnumInfo("copyTexSubImage2D: target", target);
544 if (level < 0)
545 return ErrorInvalidValue("copyTexSubImage2D: level may not be negative");
547 GLsizei maxTextureSize = MaxTextureSizeForTarget(target);
548 if (!(maxTextureSize >> level))
549 return ErrorInvalidValue("copyTexSubImage2D: 2^level exceeds maximum texture size");
551 if (width < 0 || height < 0)
552 return ErrorInvalidValue("copyTexSubImage2D: width and height may not be negative");
554 if (xoffset < 0 || yoffset < 0)
555 return ErrorInvalidValue("copyTexSubImage2D: xoffset and yoffset may not be negative");
557 WebGLTexture *tex = activeBoundTextureForTarget(target);
558 if (!tex)
559 return ErrorInvalidOperation("copyTexSubImage2D: no texture bound to this target");
561 if (!tex->HasImageInfoAt(target, level))
562 return ErrorInvalidOperation("copyTexSubImage2D: no texture image previously defined for this level and face");
564 const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
565 GLsizei texWidth = imageInfo.Width();
566 GLsizei texHeight = imageInfo.Height();
568 if (xoffset + width > texWidth || xoffset + width < 0)
569 return ErrorInvalidValue("copyTexSubImage2D: xoffset+width is too large");
571 if (yoffset + height > texHeight || yoffset + height < 0)
572 return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large");
574 if (!mBoundFramebuffer)
575 ClearBackbufferIfNeeded();
577 if (imageInfo.HasUninitializedImageData()) {
578 tex->DoDeferredImageInitialization(target, level);
581 return CopyTexSubImage2D_base(target, level, imageInfo.WebGLFormat(), xoffset, yoffset, x, y, width, height, true);
585 already_AddRefed<WebGLProgram>
586 WebGLContext::CreateProgram()
588 if (IsContextLost())
589 return nullptr;
590 nsRefPtr<WebGLProgram> globj = new WebGLProgram(this);
591 return globj.forget();
594 already_AddRefed<WebGLShader>
595 WebGLContext::CreateShader(GLenum type)
597 if (IsContextLost())
598 return nullptr;
600 if (type != LOCAL_GL_VERTEX_SHADER &&
601 type != LOCAL_GL_FRAGMENT_SHADER)
603 ErrorInvalidEnumInfo("createShader: type", type);
604 return nullptr;
607 nsRefPtr<WebGLShader> shader = new WebGLShader(this, type);
608 return shader.forget();
611 void
612 WebGLContext::CullFace(GLenum face)
614 if (IsContextLost())
615 return;
617 if (!ValidateFaceEnum(face, "cullFace"))
618 return;
620 MakeContextCurrent();
621 gl->fCullFace(face);
624 void
625 WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf)
627 if (IsContextLost())
628 return;
630 if (!ValidateObjectAllowDeletedOrNull("deleteFramebuffer", fbuf))
631 return;
633 if (!fbuf || fbuf->IsDeleted())
634 return;
636 fbuf->RequestDelete();
638 if (mBoundFramebuffer == fbuf)
639 BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
640 static_cast<WebGLFramebuffer*>(nullptr));
643 void
644 WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer *rbuf)
646 if (IsContextLost())
647 return;
649 if (!ValidateObjectAllowDeletedOrNull("deleteRenderbuffer", rbuf))
650 return;
652 if (!rbuf || rbuf->IsDeleted())
653 return;
655 if (mBoundFramebuffer)
656 mBoundFramebuffer->DetachRenderbuffer(rbuf);
658 // Invalidate framebuffer status cache
659 rbuf->NotifyFBsStatusChanged();
661 if (mBoundRenderbuffer == rbuf)
662 BindRenderbuffer(LOCAL_GL_RENDERBUFFER,
663 static_cast<WebGLRenderbuffer*>(nullptr));
665 rbuf->RequestDelete();
668 void
669 WebGLContext::DeleteTexture(WebGLTexture *tex)
671 if (IsContextLost())
672 return;
674 if (!ValidateObjectAllowDeletedOrNull("deleteTexture", tex))
675 return;
677 if (!tex || tex->IsDeleted())
678 return;
680 if (mBoundFramebuffer)
681 mBoundFramebuffer->DetachTexture(tex);
683 // Invalidate framebuffer status cache
684 tex->NotifyFBsStatusChanged();
686 GLuint activeTexture = mActiveTexture;
687 for (int32_t i = 0; i < mGLMaxTextureUnits; i++) {
688 if ((tex->Target() == LOCAL_GL_TEXTURE_2D && mBound2DTextures[i] == tex) ||
689 (tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP && mBoundCubeMapTextures[i] == tex))
691 ActiveTexture(LOCAL_GL_TEXTURE0 + i);
692 BindTexture(tex->Target(), static_cast<WebGLTexture*>(nullptr));
695 ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
697 tex->RequestDelete();
700 void
701 WebGLContext::DeleteProgram(WebGLProgram *prog)
703 if (IsContextLost())
704 return;
706 if (!ValidateObjectAllowDeletedOrNull("deleteProgram", prog))
707 return;
709 if (!prog || prog->IsDeleted())
710 return;
712 prog->RequestDelete();
715 void
716 WebGLContext::DeleteShader(WebGLShader *shader)
718 if (IsContextLost())
719 return;
721 if (!ValidateObjectAllowDeletedOrNull("deleteShader", shader))
722 return;
724 if (!shader || shader->IsDeleted())
725 return;
727 shader->RequestDelete();
730 void
731 WebGLContext::DetachShader(WebGLProgram *program, WebGLShader *shader)
733 if (IsContextLost())
734 return;
736 if (!ValidateObject("detachShader: program", program) ||
737 // it's valid to attempt to detach a deleted shader, since it's
738 // still a shader
739 !ValidateObjectAllowDeleted("detashShader: shader", shader))
740 return;
742 if (!program->DetachShader(shader))
743 return ErrorInvalidOperation("detachShader: shader is not attached");
746 void
747 WebGLContext::DepthFunc(GLenum func)
749 if (IsContextLost())
750 return;
752 if (!ValidateComparisonEnum(func, "depthFunc"))
753 return;
755 MakeContextCurrent();
756 gl->fDepthFunc(func);
759 void
760 WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar)
762 if (IsContextLost())
763 return;
765 if (zNear > zFar)
766 return ErrorInvalidOperation("depthRange: the near value is greater than the far value!");
768 MakeContextCurrent();
769 gl->fDepthRange(zNear, zFar);
772 void
773 WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum rbtarget, WebGLRenderbuffer *wrb)
775 if (IsContextLost())
776 return;
778 if (!mBoundFramebuffer)
779 return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0");
781 return mBoundFramebuffer->FramebufferRenderbuffer(target, attachment, rbtarget, wrb);
784 void
785 WebGLContext::FramebufferTexture2D(GLenum target,
786 GLenum attachment,
787 GLenum textarget,
788 WebGLTexture *tobj,
789 GLint level)
791 if (IsContextLost())
792 return;
794 if (!mBoundFramebuffer)
795 return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0");
797 return mBoundFramebuffer->FramebufferTexture2D(target, attachment, textarget, tobj, level);
800 void
801 WebGLContext::FrontFace(GLenum mode)
803 if (IsContextLost())
804 return;
806 switch (mode) {
807 case LOCAL_GL_CW:
808 case LOCAL_GL_CCW:
809 break;
810 default:
811 return ErrorInvalidEnumInfo("frontFace: mode", mode);
814 MakeContextCurrent();
815 gl->fFrontFace(mode);
818 already_AddRefed<WebGLActiveInfo>
819 WebGLContext::GetActiveAttrib(WebGLProgram *prog, uint32_t index)
821 if (IsContextLost())
822 return nullptr;
824 if (!ValidateObject("getActiveAttrib: program", prog))
825 return nullptr;
827 MakeContextCurrent();
829 GLint len = 0;
830 GLuint progname = prog->GLName();;
831 gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len);
832 if (len == 0)
833 return nullptr;
835 nsAutoArrayPtr<char> name(new char[len]);
836 GLint attrsize = 0;
837 GLuint attrtype = 0;
839 gl->fGetActiveAttrib(progname, index, len, &len, &attrsize, &attrtype, name);
840 if (attrsize == 0 || attrtype == 0) {
841 return nullptr;
844 nsCString reverseMappedName;
845 prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName);
847 nsRefPtr<WebGLActiveInfo> retActiveInfo =
848 new WebGLActiveInfo(attrsize, attrtype, reverseMappedName);
849 return retActiveInfo.forget();
852 void
853 WebGLContext::GenerateMipmap(GLenum target)
855 if (IsContextLost())
856 return;
858 if (!ValidateTextureTargetEnum(target, "generateMipmap"))
859 return;
861 WebGLTexture *tex = activeBoundTextureForTarget(target);
863 if (!tex)
864 return ErrorInvalidOperation("generateMipmap: No texture is bound to this target.");
866 GLenum imageTarget = (target == LOCAL_GL_TEXTURE_2D) ? LOCAL_GL_TEXTURE_2D
867 : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
868 if (!tex->HasImageInfoAt(imageTarget, 0))
870 return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
873 if (!tex->IsFirstImagePowerOfTwo())
874 return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
876 GLenum webGLFormat = tex->ImageInfoAt(imageTarget, 0).WebGLFormat();
877 if (IsTextureFormatCompressed(webGLFormat))
878 return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
880 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
881 (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat)))
883 return ErrorInvalidOperation("generateMipmap: "
884 "A texture that has a base internal format of "
885 "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
888 if (!tex->AreAllLevel0ImageInfosEqual())
889 return ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type.");
891 tex->SetGeneratedMipmap();
893 MakeContextCurrent();
895 if (gl->WorkAroundDriverBugs()) {
896 // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we
897 // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance
898 // overhead so we do it unconditionally.
900 // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105.
901 gl->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST_MIPMAP_NEAREST);
902 gl->fGenerateMipmap(target);
903 gl->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, tex->MinFilter());
904 } else {
905 gl->fGenerateMipmap(target);
909 already_AddRefed<WebGLActiveInfo>
910 WebGLContext::GetActiveUniform(WebGLProgram *prog, uint32_t index)
912 if (IsContextLost())
913 return nullptr;
915 if (!ValidateObject("getActiveUniform: program", prog))
916 return nullptr;
918 MakeContextCurrent();
920 GLint len = 0;
921 GLuint progname = prog->GLName();
922 gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &len);
923 if (len == 0)
924 return nullptr;
926 nsAutoArrayPtr<char> name(new char[len]);
928 GLint usize = 0;
929 GLuint utype = 0;
931 gl->fGetActiveUniform(progname, index, len, &len, &usize, &utype, name);
932 if (len == 0 || usize == 0 || utype == 0) {
933 return nullptr;
936 nsCString reverseMappedName;
937 prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName);
939 // OpenGL ES 2.0 specifies that if foo is a uniform array, GetActiveUniform returns its name as "foo[0]".
940 // See section 2.10 page 35 in the OpenGL ES 2.0.24 specification:
942 // > If the active uniform is an array, the uniform name returned in name will always
943 // > be the name of the uniform array appended with "[0]".
945 // There is no such requirement in the OpenGL (non-ES) spec and indeed we have OpenGL implementations returning
946 // "foo" instead of "foo[0]". So, when implementing WebGL on top of desktop OpenGL, we must check if the
947 // returned name ends in [0], and if it doesn't, append that.
949 // In principle we don't need to do that on OpenGL ES, but this is such a tricky difference between the ES and non-ES
950 // specs that it seems probable that some ES implementers will overlook it. Since the work-around is quite cheap,
951 // we do it unconditionally.
952 if (usize > 1 && reverseMappedName.CharAt(reverseMappedName.Length()-1) != ']')
953 reverseMappedName.AppendLiteral("[0]");
955 nsRefPtr<WebGLActiveInfo> retActiveInfo =
956 new WebGLActiveInfo(usize, utype, reverseMappedName);
957 return retActiveInfo.forget();
960 void
961 WebGLContext::GetAttachedShaders(WebGLProgram *prog,
962 Nullable< nsTArray<WebGLShader*> > &retval)
964 retval.SetNull();
965 if (IsContextLost())
966 return;
968 if (!ValidateObjectAllowNull("getAttachedShaders", prog))
969 return;
971 MakeContextCurrent();
973 if (!prog) {
974 retval.SetNull();
975 ErrorInvalidValue("getAttachedShaders: invalid program");
976 } else if (prog->AttachedShaders().Length() == 0) {
977 retval.SetValue().TruncateLength(0);
978 } else {
979 retval.SetValue().AppendElements(prog->AttachedShaders());
983 GLint
984 WebGLContext::GetAttribLocation(WebGLProgram *prog, const nsAString& name)
986 if (IsContextLost())
987 return -1;
989 if (!ValidateObject("getAttribLocation: program", prog))
990 return -1;
992 if (!ValidateGLSLVariableName(name, "getAttribLocation"))
993 return -1;
995 NS_LossyConvertUTF16toASCII cname(name);
996 nsCString mappedName;
997 prog->MapIdentifier(cname, &mappedName);
999 GLuint progname = prog->GLName();
1001 MakeContextCurrent();
1002 return gl->fGetAttribLocation(progname, mappedName.get());
1005 JS::Value
1006 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
1008 if (IsContextLost())
1009 return JS::NullValue();
1011 if (target != LOCAL_GL_ARRAY_BUFFER && target != LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
1012 ErrorInvalidEnumInfo("getBufferParameter: target", target);
1013 return JS::NullValue();
1016 MakeContextCurrent();
1018 switch (pname) {
1019 case LOCAL_GL_BUFFER_SIZE:
1020 case LOCAL_GL_BUFFER_USAGE:
1022 GLint i = 0;
1023 gl->fGetBufferParameteriv(target, pname, &i);
1024 if (pname == LOCAL_GL_BUFFER_SIZE) {
1025 return JS::Int32Value(i);
1028 MOZ_ASSERT(pname == LOCAL_GL_BUFFER_USAGE);
1029 return JS::NumberValue(uint32_t(i));
1031 break;
1033 default:
1034 ErrorInvalidEnumInfo("getBufferParameter: parameter", pname);
1037 return JS::NullValue();
1040 JS::Value
1041 WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
1042 GLenum target,
1043 GLenum attachment,
1044 GLenum pname,
1045 ErrorResult& rv)
1047 if (IsContextLost())
1048 return JS::NullValue();
1050 if (target != LOCAL_GL_FRAMEBUFFER) {
1051 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: target", target);
1052 return JS::NullValue();
1055 if (!mBoundFramebuffer) {
1056 ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query framebuffer 0");
1057 return JS::NullValue();
1060 if (attachment != LOCAL_GL_DEPTH_ATTACHMENT &&
1061 attachment != LOCAL_GL_STENCIL_ATTACHMENT &&
1062 attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
1064 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
1066 if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
1067 attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mGLMaxColorAttachments))
1069 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: attachment", attachment);
1070 return JS::NullValue();
1073 mBoundFramebuffer->EnsureColorAttachments(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
1075 else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
1077 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: attachment", attachment);
1078 return JS::NullValue();
1082 MakeContextCurrent();
1084 const WebGLFramebuffer::Attachment& fba = mBoundFramebuffer->GetAttachment(attachment);
1086 if (fba.Renderbuffer()) {
1087 switch (pname) {
1088 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
1089 if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
1090 const GLenum internalFormat = fba.Renderbuffer()->InternalFormat();
1091 return (internalFormat == LOCAL_GL_SRGB_EXT ||
1092 internalFormat == LOCAL_GL_SRGB_ALPHA_EXT ||
1093 internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT) ?
1094 JS::NumberValue(uint32_t(LOCAL_GL_SRGB_EXT)) :
1095 JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
1097 break;
1099 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
1100 return JS::NumberValue(uint32_t(LOCAL_GL_RENDERBUFFER));
1102 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
1103 return WebGLObjectAsJSValue(cx, fba.Renderbuffer(), rv);
1105 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: {
1106 if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) &&
1107 !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
1109 break;
1112 if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1113 ErrorInvalidOperation("getFramebufferAttachmentParameter: Cannot get component"
1114 " type of a depth-stencil attachment.");
1115 return JS::NullValue();
1118 if (!fba.IsComplete())
1119 return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
1121 uint32_t ret = LOCAL_GL_NONE;
1122 switch (fba.Renderbuffer()->InternalFormat()) {
1123 case LOCAL_GL_RGBA4:
1124 case LOCAL_GL_RGB5_A1:
1125 case LOCAL_GL_RGB565:
1126 case LOCAL_GL_SRGB8_ALPHA8:
1127 ret = LOCAL_GL_UNSIGNED_NORMALIZED;
1128 break;
1129 case LOCAL_GL_RGB16F:
1130 case LOCAL_GL_RGBA16F:
1131 case LOCAL_GL_RGB32F:
1132 case LOCAL_GL_RGBA32F:
1133 ret = LOCAL_GL_FLOAT;
1134 break;
1135 case LOCAL_GL_DEPTH_COMPONENT16:
1136 case LOCAL_GL_STENCIL_INDEX8:
1137 ret = LOCAL_GL_UNSIGNED_INT;
1138 break;
1139 default:
1140 MOZ_ASSERT(false, "Unhandled RB component type.");
1141 break;
1143 return JS::NumberValue(uint32_t(ret));
1147 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
1148 return JS::NullValue();
1149 } else if (fba.Texture()) {
1150 switch (pname) {
1151 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
1152 if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
1153 const GLenum webGLFormat =
1154 fba.Texture()->ImageInfoBase().WebGLFormat();
1155 return (webGLFormat == LOCAL_GL_SRGB ||
1156 webGLFormat == LOCAL_GL_SRGB_ALPHA) ?
1157 JS::NumberValue(uint32_t(LOCAL_GL_SRGB)) :
1158 JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
1160 break;
1162 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
1163 return JS::NumberValue(uint32_t(LOCAL_GL_TEXTURE));
1165 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
1166 return WebGLObjectAsJSValue(cx, fba.Texture(), rv);
1168 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
1169 return JS::Int32Value(fba.TexImageLevel());
1171 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: {
1172 GLenum face = fba.TexImageTarget();
1173 if (face == LOCAL_GL_TEXTURE_2D)
1174 face = 0;
1175 return JS::Int32Value(face);
1178 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: {
1179 if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) &&
1180 !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
1182 break;
1185 if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1186 ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot component"
1187 " type of depth-stencil attachments.");
1188 return JS::NullValue();
1191 if (!fba.IsComplete())
1192 return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
1194 uint32_t ret = LOCAL_GL_NONE;
1195 GLenum type = fba.Texture()->ImageInfoAt(fba.TexImageTarget(),
1196 fba.TexImageLevel()).WebGLType();
1197 switch (type) {
1198 case LOCAL_GL_UNSIGNED_BYTE:
1199 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
1200 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
1201 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
1202 ret = LOCAL_GL_UNSIGNED_NORMALIZED;
1203 break;
1204 case LOCAL_GL_FLOAT:
1205 case LOCAL_GL_HALF_FLOAT_OES:
1206 ret = LOCAL_GL_FLOAT;
1207 break;
1208 case LOCAL_GL_UNSIGNED_SHORT:
1209 case LOCAL_GL_UNSIGNED_INT:
1210 ret = LOCAL_GL_UNSIGNED_INT;
1211 break;
1212 default:
1213 MOZ_ASSERT(false, "Unhandled RB component type.");
1214 break;
1216 return JS::NumberValue(uint32_t(ret));
1220 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
1221 return JS::NullValue();
1222 } else {
1223 switch (pname) {
1224 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
1225 return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
1227 default:
1228 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
1229 return JS::NullValue();
1233 return JS::NullValue();
1236 JS::Value
1237 WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname)
1239 if (IsContextLost())
1240 return JS::NullValue();
1242 if (target != LOCAL_GL_RENDERBUFFER) {
1243 ErrorInvalidEnumInfo("getRenderbufferParameter: target", target);
1244 return JS::NullValue();
1247 if (!mBoundRenderbuffer) {
1248 ErrorInvalidOperation("getRenderbufferParameter: no render buffer is bound");
1249 return JS::NullValue();
1252 MakeContextCurrent();
1254 switch (pname) {
1255 case LOCAL_GL_RENDERBUFFER_WIDTH:
1256 case LOCAL_GL_RENDERBUFFER_HEIGHT:
1257 case LOCAL_GL_RENDERBUFFER_RED_SIZE:
1258 case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
1259 case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
1260 case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
1261 case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
1262 case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
1264 // RB emulation means we have to ask the RB itself.
1265 GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname);
1266 return JS::Int32Value(i);
1268 case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
1270 return JS::NumberValue(mBoundRenderbuffer->InternalFormat());
1272 default:
1273 ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname);
1276 return JS::NullValue();
1279 already_AddRefed<WebGLTexture>
1280 WebGLContext::CreateTexture()
1282 if (IsContextLost())
1283 return nullptr;
1284 nsRefPtr<WebGLTexture> globj = new WebGLTexture(this);
1285 return globj.forget();
1288 static GLenum
1289 GetAndClearError(GLenum* errorVar)
1291 MOZ_ASSERT(errorVar);
1292 GLenum ret = *errorVar;
1293 *errorVar = LOCAL_GL_NO_ERROR;
1294 return ret;
1297 GLenum
1298 WebGLContext::GetError()
1300 /* WebGL 1.0: Section 5.14.3: Setting and getting state:
1301 * If the context's webgl context lost flag is set, returns
1302 * CONTEXT_LOST_WEBGL the first time this method is called.
1303 * Afterward, returns NO_ERROR until the context has been
1304 * restored.
1306 * WEBGL_lose_context:
1307 * [When this extension is enabled: ] loseContext and
1308 * restoreContext are allowed to generate INVALID_OPERATION errors
1309 * even when the context is lost.
1312 if (IsContextLost()) {
1313 if (mEmitContextLostErrorOnce) {
1314 mEmitContextLostErrorOnce = false;
1315 return LOCAL_GL_CONTEXT_LOST;
1317 // Don't return yet, since WEBGL_lose_contexts contradicts the
1318 // original spec, and allows error generation while lost.
1321 GLenum err = GetAndClearError(&mWebGLError);
1322 if (err != LOCAL_GL_NO_ERROR)
1323 return err;
1325 if (IsContextLost())
1326 return LOCAL_GL_NO_ERROR;
1328 // Either no WebGL-side error, or it's already been cleared.
1329 // UnderlyingGL-side errors, now.
1331 MakeContextCurrent();
1332 GetAndFlushUnderlyingGLErrors();
1334 err = GetAndClearError(&mUnderlyingGLError);
1335 return err;
1338 JS::Value
1339 WebGLContext::GetProgramParameter(WebGLProgram *prog, GLenum pname)
1341 if (IsContextLost())
1342 return JS::NullValue();
1344 if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
1345 return JS::NullValue();
1347 GLuint progname = prog->GLName();
1349 MakeContextCurrent();
1351 switch (pname) {
1352 case LOCAL_GL_ATTACHED_SHADERS:
1353 case LOCAL_GL_ACTIVE_UNIFORMS:
1354 case LOCAL_GL_ACTIVE_ATTRIBUTES:
1356 GLint i = 0;
1357 gl->fGetProgramiv(progname, pname, &i);
1358 return JS::Int32Value(i);
1360 case LOCAL_GL_DELETE_STATUS:
1361 return JS::BooleanValue(prog->IsDeleteRequested());
1362 case LOCAL_GL_LINK_STATUS:
1364 return JS::BooleanValue(prog->LinkStatus());
1366 case LOCAL_GL_VALIDATE_STATUS:
1368 GLint i = 0;
1369 #ifdef XP_MACOSX
1370 // See comment in ValidateProgram below.
1371 if (gl->WorkAroundDriverBugs())
1372 i = 1;
1373 else
1374 gl->fGetProgramiv(progname, pname, &i);
1375 #else
1376 gl->fGetProgramiv(progname, pname, &i);
1377 #endif
1378 return JS::BooleanValue(bool(i));
1380 break;
1382 default:
1383 ErrorInvalidEnumInfo("getProgramParameter: parameter", pname);
1386 return JS::NullValue();
1389 void
1390 WebGLContext::GetProgramInfoLog(WebGLProgram *prog, nsAString& retval)
1392 nsAutoCString s;
1393 GetProgramInfoLog(prog, s);
1394 if (s.IsVoid())
1395 retval.SetIsVoid(true);
1396 else
1397 CopyASCIItoUTF16(s, retval);
1400 void
1401 WebGLContext::GetProgramInfoLog(WebGLProgram *prog, nsACString& retval)
1403 if (IsContextLost())
1405 retval.SetIsVoid(true);
1406 return;
1409 if (!ValidateObject("getProgramInfoLog: program", prog)) {
1410 retval.Truncate();
1411 return;
1414 GLuint progname = prog->GLName();
1416 MakeContextCurrent();
1418 GLint k = -1;
1419 gl->fGetProgramiv(progname, LOCAL_GL_INFO_LOG_LENGTH, &k);
1420 if (k == -1) {
1421 // If GetProgramiv doesn't modify |k|,
1422 // it's because there was a GL error.
1423 // GetProgramInfoLog should return null on error. (Bug 746740)
1424 retval.SetIsVoid(true);
1425 return;
1428 if (k == 0) {
1429 retval.Truncate();
1430 return;
1433 retval.SetCapacity(k);
1434 gl->fGetProgramInfoLog(progname, k, &k, (char*) retval.BeginWriting());
1435 retval.SetLength(k);
1438 // here we have to support all pnames with both int and float params.
1439 // See this discussion:
1440 // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
1441 void WebGLContext::TexParameter_base(GLenum target, GLenum pname,
1442 GLint *intParamPtr,
1443 GLfloat *floatParamPtr)
1445 MOZ_ASSERT(intParamPtr || floatParamPtr);
1447 if (IsContextLost())
1448 return;
1450 GLint intParam = intParamPtr ? *intParamPtr : GLint(*floatParamPtr);
1451 GLfloat floatParam = floatParamPtr ? *floatParamPtr : GLfloat(*intParamPtr);
1453 if (!ValidateTextureTargetEnum(target, "texParameter: target"))
1454 return;
1456 WebGLTexture *tex = activeBoundTextureForTarget(target);
1457 if (!tex)
1458 return ErrorInvalidOperation("texParameter: no texture is bound to this target");
1460 bool pnameAndParamAreIncompatible = false;
1461 bool paramValueInvalid = false;
1463 switch (pname) {
1464 case LOCAL_GL_TEXTURE_MIN_FILTER:
1465 switch (intParam) {
1466 case LOCAL_GL_NEAREST:
1467 case LOCAL_GL_LINEAR:
1468 case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
1469 case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
1470 case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
1471 case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
1472 tex->SetMinFilter(intParam);
1473 break;
1474 default:
1475 pnameAndParamAreIncompatible = true;
1477 break;
1478 case LOCAL_GL_TEXTURE_MAG_FILTER:
1479 switch (intParam) {
1480 case LOCAL_GL_NEAREST:
1481 case LOCAL_GL_LINEAR:
1482 tex->SetMagFilter(intParam);
1483 break;
1484 default:
1485 pnameAndParamAreIncompatible = true;
1487 break;
1488 case LOCAL_GL_TEXTURE_WRAP_S:
1489 switch (intParam) {
1490 case LOCAL_GL_CLAMP_TO_EDGE:
1491 case LOCAL_GL_MIRRORED_REPEAT:
1492 case LOCAL_GL_REPEAT:
1493 tex->SetWrapS(intParam);
1494 break;
1495 default:
1496 pnameAndParamAreIncompatible = true;
1498 break;
1499 case LOCAL_GL_TEXTURE_WRAP_T:
1500 switch (intParam) {
1501 case LOCAL_GL_CLAMP_TO_EDGE:
1502 case LOCAL_GL_MIRRORED_REPEAT:
1503 case LOCAL_GL_REPEAT:
1504 tex->SetWrapT(intParam);
1505 break;
1506 default:
1507 pnameAndParamAreIncompatible = true;
1509 break;
1510 case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1511 if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
1512 if (floatParamPtr && floatParam < 1.f)
1513 paramValueInvalid = true;
1514 else if (intParamPtr && intParam < 1)
1515 paramValueInvalid = true;
1517 else
1518 pnameAndParamAreIncompatible = true;
1519 break;
1520 default:
1521 return ErrorInvalidEnumInfo("texParameter: pname", pname);
1524 if (pnameAndParamAreIncompatible) {
1525 if (intParamPtr)
1526 return ErrorInvalidEnum("texParameteri: pname %x and param %x (decimal %d) are mutually incompatible",
1527 pname, intParam, intParam);
1528 else
1529 return ErrorInvalidEnum("texParameterf: pname %x and param %g are mutually incompatible",
1530 pname, floatParam);
1531 } else if (paramValueInvalid) {
1532 if (intParamPtr)
1533 return ErrorInvalidValue("texParameteri: pname %x and param %x (decimal %d) is invalid",
1534 pname, intParam, intParam);
1535 else
1536 return ErrorInvalidValue("texParameterf: pname %x and param %g is invalid",
1537 pname, floatParam);
1540 MakeContextCurrent();
1541 if (intParamPtr)
1542 gl->fTexParameteri(target, pname, intParam);
1543 else
1544 gl->fTexParameterf(target, pname, floatParam);
1547 JS::Value
1548 WebGLContext::GetTexParameter(GLenum target, GLenum pname)
1550 if (IsContextLost())
1551 return JS::NullValue();
1553 MakeContextCurrent();
1555 if (!ValidateTextureTargetEnum(target, "getTexParameter: target"))
1556 return JS::NullValue();
1558 if (!activeBoundTextureForTarget(target)) {
1559 ErrorInvalidOperation("getTexParameter: no texture bound");
1560 return JS::NullValue();
1563 switch (pname) {
1564 case LOCAL_GL_TEXTURE_MIN_FILTER:
1565 case LOCAL_GL_TEXTURE_MAG_FILTER:
1566 case LOCAL_GL_TEXTURE_WRAP_S:
1567 case LOCAL_GL_TEXTURE_WRAP_T:
1569 GLint i = 0;
1570 gl->fGetTexParameteriv(target, pname, &i);
1571 return JS::NumberValue(uint32_t(i));
1573 case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1574 if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
1575 GLfloat f = 0.f;
1576 gl->fGetTexParameterfv(target, pname, &f);
1577 return JS::DoubleValue(f);
1580 ErrorInvalidEnumInfo("getTexParameter: parameter", pname);
1581 break;
1583 default:
1584 ErrorInvalidEnumInfo("getTexParameter: parameter", pname);
1587 return JS::NullValue();
1590 JS::Value
1591 WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog,
1592 WebGLUniformLocation *location)
1594 if (IsContextLost())
1595 return JS::NullValue();
1597 if (!ValidateObject("getUniform: program", prog))
1598 return JS::NullValue();
1600 if (!ValidateObject("getUniform: location", location))
1601 return JS::NullValue();
1603 if (location->Program() != prog) {
1604 ErrorInvalidValue("getUniform: this uniform location corresponds to another program");
1605 return JS::NullValue();
1608 if (location->ProgramGeneration() != prog->Generation()) {
1609 ErrorInvalidOperation("getUniform: this uniform location is obsolete since the program has been relinked");
1610 return JS::NullValue();
1613 GLuint progname = prog->GLName();
1615 MakeContextCurrent();
1617 GLint uniforms = 0;
1618 GLint uniformNameMaxLength = 0;
1619 gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS, &uniforms);
1620 gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformNameMaxLength);
1622 // we now need the type info to switch between fGetUniformfv and fGetUniformiv
1623 // the only way to get that is to iterate through all active uniforms by index until
1624 // one matches the given uniform location.
1625 GLenum uniformType = 0;
1626 nsAutoArrayPtr<GLchar> uniformName(new GLchar[uniformNameMaxLength]);
1627 // this buffer has 16 more bytes to be able to store [index] at the end.
1628 nsAutoArrayPtr<GLchar> uniformNameBracketIndex(new GLchar[uniformNameMaxLength + 16]);
1630 GLint index;
1631 for (index = 0; index < uniforms; ++index) {
1632 GLsizei length;
1633 GLint size;
1634 gl->fGetActiveUniform(progname, index, uniformNameMaxLength, &length,
1635 &size, &uniformType, uniformName);
1636 if (gl->fGetUniformLocation(progname, uniformName) == location->Location())
1637 break;
1639 // now we handle the case of array uniforms. In that case, fGetActiveUniform returned as 'size'
1640 // the biggest index used plus one, so we need to loop over that. The 0 index has already been handled above,
1641 // so we can start at one. For each index, we construct the string uniformName + "[" + index + "]".
1642 if (size > 1) {
1643 bool found_it = false;
1644 if (uniformName[length - 1] == ']') { // if uniformName ends in [0]
1645 // remove the [0] at the end
1646 length -= 3;
1647 uniformName[length] = 0;
1649 for (GLint arrayIndex = 1; arrayIndex < size; arrayIndex++) {
1650 sprintf(uniformNameBracketIndex.get(), "%s[%d]", uniformName.get(), arrayIndex);
1651 if (gl->fGetUniformLocation(progname, uniformNameBracketIndex) == location->Location()) {
1652 found_it = true;
1653 break;
1656 if (found_it) break;
1660 if (index == uniforms) {
1661 GenerateWarning("getUniform: internal error: hit an OpenGL driver bug");
1662 return JS::NullValue();
1665 GLenum baseType;
1666 GLint unitSize;
1667 if (!BaseTypeAndSizeFromUniformType(uniformType, &baseType, &unitSize)) {
1668 GenerateWarning("getUniform: internal error: unknown uniform type 0x%x", uniformType);
1669 return JS::NullValue();
1672 // this should never happen
1673 if (unitSize > 16) {
1674 GenerateWarning("getUniform: internal error: unexpected uniform unit size %d", unitSize);
1675 return JS::NullValue();
1678 if (baseType == LOCAL_GL_FLOAT) {
1679 GLfloat fv[16] = { GLfloat(0) };
1680 gl->fGetUniformfv(progname, location->Location(), fv);
1681 if (unitSize == 1) {
1682 return JS::DoubleValue(fv[0]);
1683 } else {
1684 JSObject* obj = Float32Array::Create(cx, this, unitSize, fv);
1685 if (!obj) {
1686 ErrorOutOfMemory("getUniform: out of memory");
1687 return JS::NullValue();
1689 return JS::ObjectOrNullValue(obj);
1691 } else if (baseType == LOCAL_GL_INT) {
1692 GLint iv[16] = { 0 };
1693 gl->fGetUniformiv(progname, location->Location(), iv);
1694 if (unitSize == 1) {
1695 return JS::Int32Value(iv[0]);
1696 } else {
1697 JSObject* obj = Int32Array::Create(cx, this, unitSize, iv);
1698 if (!obj) {
1699 ErrorOutOfMemory("getUniform: out of memory");
1700 return JS::NullValue();
1702 return JS::ObjectOrNullValue(obj);
1704 } else if (baseType == LOCAL_GL_BOOL) {
1705 GLint iv[16] = { 0 };
1706 gl->fGetUniformiv(progname, location->Location(), iv);
1707 if (unitSize == 1) {
1708 return JS::BooleanValue(iv[0] ? true : false);
1709 } else {
1710 bool uv[16];
1711 for (int k = 0; k < unitSize; k++)
1712 uv[k] = iv[k];
1713 JS::Rooted<JS::Value> val(cx);
1714 // Be careful: we don't want to convert all of |uv|!
1715 if (!ToJSValue(cx, uv, unitSize, &val)) {
1716 ErrorOutOfMemory("getUniform: out of memory");
1717 return JS::NullValue();
1719 return val;
1723 // Else preserving behavior, but I'm not sure this is correct per spec
1724 return JS::UndefinedValue();
1727 already_AddRefed<WebGLUniformLocation>
1728 WebGLContext::GetUniformLocation(WebGLProgram *prog, const nsAString& name)
1730 if (IsContextLost())
1731 return nullptr;
1733 if (!ValidateObject("getUniformLocation: program", prog))
1734 return nullptr;
1736 if (!ValidateGLSLVariableName(name, "getUniformLocation"))
1737 return nullptr;
1739 NS_LossyConvertUTF16toASCII cname(name);
1740 nsCString mappedName;
1741 prog->MapIdentifier(cname, &mappedName);
1743 GLuint progname = prog->GLName();
1744 MakeContextCurrent();
1745 GLint intlocation = gl->fGetUniformLocation(progname, mappedName.get());
1747 nsRefPtr<WebGLUniformLocation> loc;
1748 if (intlocation >= 0) {
1749 WebGLUniformInfo info = prog->GetUniformInfoForMappedIdentifier(mappedName);
1750 loc = new WebGLUniformLocation(this,
1751 prog,
1752 intlocation,
1753 info);
1755 return loc.forget();
1758 void
1759 WebGLContext::Hint(GLenum target, GLenum mode)
1761 if (IsContextLost())
1762 return;
1764 bool isValid = false;
1766 switch (target) {
1767 case LOCAL_GL_GENERATE_MIPMAP_HINT:
1768 isValid = true;
1769 break;
1770 case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
1771 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
1772 isValid = true;
1773 break;
1776 if (!isValid)
1777 return ErrorInvalidEnum("hint: invalid hint");
1779 MakeContextCurrent();
1780 gl->fHint(target, mode);
1783 bool
1784 WebGLContext::IsFramebuffer(WebGLFramebuffer *fb)
1786 if (IsContextLost())
1787 return false;
1789 return ValidateObjectAllowDeleted("isFramebuffer", fb) &&
1790 !fb->IsDeleted() &&
1791 fb->HasEverBeenBound();
1794 bool
1795 WebGLContext::IsProgram(WebGLProgram *prog)
1797 if (IsContextLost())
1798 return false;
1800 return ValidateObjectAllowDeleted("isProgram", prog) && !prog->IsDeleted();
1803 bool
1804 WebGLContext::IsRenderbuffer(WebGLRenderbuffer *rb)
1806 if (IsContextLost())
1807 return false;
1809 return ValidateObjectAllowDeleted("isRenderBuffer", rb) &&
1810 !rb->IsDeleted() &&
1811 rb->HasEverBeenBound();
1814 bool
1815 WebGLContext::IsShader(WebGLShader *shader)
1817 if (IsContextLost())
1818 return false;
1820 return ValidateObjectAllowDeleted("isShader", shader) &&
1821 !shader->IsDeleted();
1824 bool
1825 WebGLContext::IsTexture(WebGLTexture *tex)
1827 if (IsContextLost())
1828 return false;
1830 return ValidateObjectAllowDeleted("isTexture", tex) &&
1831 !tex->IsDeleted() &&
1832 tex->HasEverBeenBound();
1835 // Try to bind an attribute that is an array to location 0:
1836 bool WebGLContext::BindArrayAttribToLocation0(WebGLProgram *program)
1838 if (mBoundVertexArray->IsAttribArrayEnabled(0)) {
1839 return false;
1842 GLint leastArrayLocation = -1;
1844 std::map<GLint, nsCString>::iterator itr;
1845 for (itr = program->mActiveAttribMap.begin();
1846 itr != program->mActiveAttribMap.end();
1847 itr++) {
1848 int32_t index = itr->first;
1849 if (mBoundVertexArray->IsAttribArrayEnabled(index) &&
1850 index < leastArrayLocation)
1852 leastArrayLocation = index;
1856 if (leastArrayLocation > 0) {
1857 nsCString& attrName = program->mActiveAttribMap.find(leastArrayLocation)->second;
1858 const char* attrNameCStr = attrName.get();
1859 gl->fBindAttribLocation(program->GLName(), 0, attrNameCStr);
1860 return true;
1862 return false;
1865 void
1866 WebGLContext::LinkProgram(WebGLProgram *program)
1868 if (IsContextLost())
1869 return;
1871 if (!ValidateObject("linkProgram", program))
1872 return;
1874 InvalidateBufferFetching(); // we do it early in this function
1875 // as some of the validation below changes program state
1877 GLuint progname = program->GLName();
1879 if (!program->NextGeneration()) {
1880 // XXX throw?
1881 return;
1884 if (!program->HasBothShaderTypesAttached()) {
1885 GenerateWarning("linkProgram: this program doesn't have both a vertex shader"
1886 " and a fragment shader");
1887 program->SetLinkStatus(false);
1888 return;
1891 // bug 777028
1892 // Mesa can't handle more than 16 samplers per program, counting each array entry.
1893 if (gl->WorkAroundDriverBugs() &&
1894 mIsMesa &&
1895 program->UpperBoundNumSamplerUniforms() > 16)
1897 GenerateWarning("Programs with more than 16 samplers are disallowed on Mesa drivers " "to avoid a Mesa crasher.");
1898 program->SetLinkStatus(false);
1899 return;
1902 bool updateInfoSucceeded = false;
1903 GLint ok = 0;
1904 if (gl->WorkAroundDriverBugs() &&
1905 program->HasBadShaderAttached())
1907 // it's a common driver bug, caught by program-test.html, that linkProgram doesn't
1908 // correctly preserve the state of an in-use program that has been attached a bad shader
1909 // see bug 777883
1910 ok = false;
1911 } else {
1912 MakeContextCurrent();
1913 gl->fLinkProgram(progname);
1914 gl->fGetProgramiv(progname, LOCAL_GL_LINK_STATUS, &ok);
1916 if (ok) {
1917 updateInfoSucceeded = program->UpdateInfo();
1918 program->SetLinkStatus(updateInfoSucceeded);
1920 if (BindArrayAttribToLocation0(program)) {
1921 GenerateWarning("linkProgram: relinking program to make attrib0 an "
1922 "array.");
1923 gl->fLinkProgram(progname);
1924 gl->fGetProgramiv(progname, LOCAL_GL_LINK_STATUS, &ok);
1925 if (ok) {
1926 updateInfoSucceeded = program->UpdateInfo();
1927 program->SetLinkStatus(updateInfoSucceeded);
1933 if (ok) {
1934 // Bug 750527
1935 if (gl->WorkAroundDriverBugs() &&
1936 updateInfoSucceeded &&
1937 gl->Vendor() == gl::GLVendor::NVIDIA)
1939 if (program == mCurrentProgram)
1940 gl->fUseProgram(progname);
1942 } else {
1943 program->SetLinkStatus(false);
1945 if (ShouldGenerateWarnings()) {
1947 // report shader/program infoLogs as warnings.
1948 // note that shader compilation errors can be deferred to linkProgram,
1949 // which is why we can't do anything in compileShader. In practice we could
1950 // report in compileShader the translation errors generated by ANGLE,
1951 // but it seems saner to keep a single way of obtaining shader infologs.
1953 nsAutoCString log;
1955 bool alreadyReportedShaderInfoLog = false;
1957 for (size_t i = 0; i < program->AttachedShaders().Length(); i++) {
1959 WebGLShader* shader = program->AttachedShaders()[i];
1961 if (shader->CompileStatus())
1962 continue;
1964 const char *shaderTypeName = nullptr;
1965 if (shader->ShaderType() == LOCAL_GL_VERTEX_SHADER) {
1966 shaderTypeName = "vertex";
1967 } else if (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) {
1968 shaderTypeName = "fragment";
1969 } else {
1970 // should have been validated earlier
1971 MOZ_ASSERT(false);
1972 shaderTypeName = "<unknown>";
1975 GetShaderInfoLog(shader, log);
1977 GenerateWarning("linkProgram: a %s shader used in this program failed to "
1978 "compile, with this log:\n%s\n",
1979 shaderTypeName,
1980 log.get());
1981 alreadyReportedShaderInfoLog = true;
1984 if (!alreadyReportedShaderInfoLog) {
1985 GetProgramInfoLog(program, log);
1986 if (!log.IsEmpty()) {
1987 GenerateWarning("linkProgram failed, with this log:\n%s\n",
1988 log.get());
1995 void
1996 WebGLContext::PixelStorei(GLenum pname, GLint param)
1998 if (IsContextLost())
1999 return;
2001 switch (pname) {
2002 case UNPACK_FLIP_Y_WEBGL:
2003 mPixelStoreFlipY = (param != 0);
2004 break;
2005 case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
2006 mPixelStorePremultiplyAlpha = (param != 0);
2007 break;
2008 case UNPACK_COLORSPACE_CONVERSION_WEBGL:
2009 if (param == LOCAL_GL_NONE || param == BROWSER_DEFAULT_WEBGL)
2010 mPixelStoreColorspaceConversion = param;
2011 else
2012 return ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter", param);
2013 break;
2014 case LOCAL_GL_PACK_ALIGNMENT:
2015 case LOCAL_GL_UNPACK_ALIGNMENT:
2016 if (param != 1 &&
2017 param != 2 &&
2018 param != 4 &&
2019 param != 8)
2020 return ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value");
2021 if (pname == LOCAL_GL_PACK_ALIGNMENT)
2022 mPixelStorePackAlignment = param;
2023 else if (pname == LOCAL_GL_UNPACK_ALIGNMENT)
2024 mPixelStoreUnpackAlignment = param;
2025 MakeContextCurrent();
2026 gl->fPixelStorei(pname, param);
2027 break;
2028 default:
2029 return ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
2033 void
2034 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
2035 GLsizei height, GLenum format,
2036 GLenum type, const Nullable<ArrayBufferView> &pixels,
2037 ErrorResult& rv)
2039 if (IsContextLost())
2040 return;
2042 if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) {
2043 GenerateWarning("readPixels: Not allowed");
2044 return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
2047 if (width < 0 || height < 0)
2048 return ErrorInvalidValue("readPixels: negative size passed");
2050 if (pixels.IsNull())
2051 return ErrorInvalidValue("readPixels: null destination buffer");
2053 const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
2054 GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
2055 GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
2057 uint32_t channels = 0;
2059 // Check the format param
2060 switch (format) {
2061 case LOCAL_GL_ALPHA:
2062 channels = 1;
2063 break;
2064 case LOCAL_GL_RGB:
2065 channels = 3;
2066 break;
2067 case LOCAL_GL_RGBA:
2068 channels = 4;
2069 break;
2070 default:
2071 return ErrorInvalidEnum("readPixels: Bad format");
2074 uint32_t bytesPerPixel = 0;
2075 int requiredDataType = 0;
2077 // Check the type param
2078 bool isReadTypeValid = false;
2079 bool isReadTypeFloat = false;
2080 switch (type) {
2081 case LOCAL_GL_UNSIGNED_BYTE:
2082 isReadTypeValid = true;
2083 bytesPerPixel = 1*channels;
2084 requiredDataType = js::Scalar::Uint8;
2085 break;
2086 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
2087 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
2088 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
2089 isReadTypeValid = true;
2090 bytesPerPixel = 2;
2091 requiredDataType = js::Scalar::Uint16;
2092 break;
2093 case LOCAL_GL_FLOAT:
2094 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float) ||
2095 IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float))
2097 isReadTypeValid = true;
2098 isReadTypeFloat = true;
2099 bytesPerPixel = 4*channels;
2100 requiredDataType = js::Scalar::Float32;
2102 break;
2104 if (!isReadTypeValid)
2105 return ErrorInvalidEnum("readPixels: Bad type", type);
2107 const ArrayBufferView& pixbuf = pixels.Value();
2108 int dataType = JS_GetArrayBufferViewType(pixbuf.Obj());
2110 // Check the pixels param type
2111 if (dataType != requiredDataType)
2112 return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
2114 // Check the pixels param size
2115 CheckedUint32 checked_neededByteLength =
2116 GetImageSize(height, width, bytesPerPixel, mPixelStorePackAlignment);
2118 CheckedUint32 checked_plainRowSize = CheckedUint32(width) * bytesPerPixel;
2120 CheckedUint32 checked_alignedRowSize =
2121 RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment);
2123 if (!checked_neededByteLength.isValid())
2124 return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size");
2126 // Compute length and data. Don't reenter after this point, lest the
2127 // precomputed go out of sync with the instant length/data.
2128 pixbuf.ComputeLengthAndData();
2130 uint32_t dataByteLen = pixbuf.Length();
2131 if (checked_neededByteLength.value() > dataByteLen)
2132 return ErrorInvalidOperation("readPixels: buffer too small");
2134 void* data = pixbuf.Data();
2135 if (!data) {
2136 ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
2137 return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
2140 bool isSourceTypeFloat = false;
2141 if (mBoundFramebuffer &&
2142 mBoundFramebuffer->ColorAttachmentCount() &&
2143 mBoundFramebuffer->ColorAttachment(0).IsDefined())
2145 isSourceTypeFloat = mBoundFramebuffer->ColorAttachment(0).IsReadableFloat();
2148 if (isReadTypeFloat != isSourceTypeFloat)
2149 return ErrorInvalidOperation("readPixels: Invalid type floatness");
2151 // Check the format and type params to assure they are an acceptable pair (as per spec)
2152 MakeContextCurrent();
2154 if (mBoundFramebuffer) {
2155 // prevent readback of arbitrary video memory through uninitialized renderbuffers!
2156 if (!mBoundFramebuffer->CheckAndInitializeAttachments())
2157 return ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer");
2159 GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
2160 if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
2161 return ErrorInvalidOperation("readPixels: Read source attachment doesn't have the"
2162 " correct color/depth/stencil type.");
2164 } else {
2165 ClearBackbufferIfNeeded();
2168 bool isFormatAndTypeValid = false;
2170 // OpenGL ES 2.0 $4.3.1 - IMPLEMENTATION_COLOR_READ_{TYPE/FORMAT} is a valid
2171 // combination for glReadPixels().
2172 if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
2173 GLenum implType = 0;
2174 GLenum implFormat = 0;
2176 gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE,
2177 reinterpret_cast<GLint*>(&implType));
2178 gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT,
2179 reinterpret_cast<GLint*>(&implFormat));
2181 if (type == implType && format == implFormat) {
2182 isFormatAndTypeValid = true;
2186 switch (format) {
2187 case LOCAL_GL_RGBA: {
2188 switch (type) {
2189 case LOCAL_GL_UNSIGNED_BYTE:
2190 case LOCAL_GL_FLOAT:
2191 isFormatAndTypeValid = true;
2192 break;
2194 break;
2198 if (!isFormatAndTypeValid) {
2199 return ErrorInvalidOperation("readPixels: Invalid format/type pair");
2202 // Now that the errors are out of the way, on to actually reading
2204 // If we won't be reading any pixels anyways, just skip the actual reading
2205 if (width == 0 || height == 0)
2206 return DummyFramebufferOperation("readPixels");
2208 if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
2209 // the easy case: we're not reading out-of-range pixels
2210 gl->fReadPixels(x, y, width, height, format, type, data);
2211 } else {
2212 // the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part
2213 // of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL
2214 // to do that for us, because passing out of range parameters to a buggy OpenGL implementation
2215 // could conceivably allow to read memory we shouldn't be allowed to read. So we manually initialize
2216 // the buffer to zero and compute the parameters to pass to OpenGL. We have to use an intermediate buffer
2217 // to accomodate the potentially different strides (widths).
2219 // Zero the whole pixel dest area in the destination buffer.
2220 memset(data, 0, checked_neededByteLength.value());
2222 if ( x >= framebufferWidth
2223 || x+width <= 0
2224 || y >= framebufferHeight
2225 || y+height <= 0)
2227 // we are completely outside of range, can exit now with buffer filled with zeros
2228 return DummyFramebufferOperation("readPixels");
2231 // compute the parameters of the subrect we're actually going to call glReadPixels on
2232 GLint subrect_x = std::max(x, 0);
2233 GLint subrect_end_x = std::min(x+width, framebufferWidth);
2234 GLsizei subrect_width = subrect_end_x - subrect_x;
2236 GLint subrect_y = std::max(y, 0);
2237 GLint subrect_end_y = std::min(y+height, framebufferHeight);
2238 GLsizei subrect_height = subrect_end_y - subrect_y;
2240 if (subrect_width < 0 || subrect_height < 0 ||
2241 subrect_width > width || subrect_height > height)
2242 return ErrorInvalidOperation("readPixels: integer overflow computing clipped rect size");
2244 // now we know that subrect_width is in the [0..width] interval, and same for heights.
2246 // now, same computation as above to find the size of the intermediate buffer to allocate for the subrect
2247 // no need to check again for integer overflow here, since we already know the sizes aren't greater than before
2248 uint32_t subrect_plainRowSize = subrect_width * bytesPerPixel;
2249 // There are checks above to ensure that this doesn't overflow.
2250 uint32_t subrect_alignedRowSize =
2251 RoundedToNextMultipleOf(subrect_plainRowSize, mPixelStorePackAlignment).value();
2252 uint32_t subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize;
2254 // create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer
2255 UniquePtr<GLubyte> subrect_data(new ((fallible_t())) GLubyte[subrect_byteLength]);
2256 if (!subrect_data)
2257 return ErrorOutOfMemory("readPixels: subrect_data");
2259 gl->fReadPixels(subrect_x, subrect_y, subrect_width, subrect_height,
2260 format, type, subrect_data.get());
2262 // notice that this for loop terminates because we already checked that subrect_height is at most height
2263 for (GLint y_inside_subrect = 0; y_inside_subrect < subrect_height; ++y_inside_subrect) {
2264 GLint subrect_x_in_dest_buffer = subrect_x - x;
2265 GLint subrect_y_in_dest_buffer = subrect_y - y;
2266 memcpy(static_cast<GLubyte*>(data)
2267 + checked_alignedRowSize.value() * (subrect_y_in_dest_buffer + y_inside_subrect)
2268 + bytesPerPixel * subrect_x_in_dest_buffer, // destination
2269 subrect_data.get() + subrect_alignedRowSize * y_inside_subrect, // source
2270 subrect_plainRowSize); // size
2274 // if we're reading alpha, we may need to do fixup. Note that we don't allow
2275 // GL_ALPHA to readpixels currently, but we had the code written for it already.
2276 if (format == LOCAL_GL_ALPHA ||
2277 format == LOCAL_GL_RGBA)
2279 bool needAlphaFixup;
2280 if (mBoundFramebuffer) {
2281 needAlphaFixup = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
2282 } else {
2283 needAlphaFixup = gl->GetPixelFormat().alpha == 0;
2286 if (needAlphaFixup) {
2287 if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
2288 // this is easy; it's an 0xff memset per row
2289 uint8_t *row = static_cast<uint8_t*>(data);
2290 for (GLint j = 0; j < height; ++j) {
2291 memset(row, 0xff, checked_plainRowSize.value());
2292 row += checked_alignedRowSize.value();
2294 } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
2295 // this is harder, we need to just set the alpha byte here
2296 uint8_t *row = static_cast<uint8_t*>(data);
2297 for (GLint j = 0; j < height; ++j) {
2298 uint8_t *rowp = row;
2299 #if MOZ_LITTLE_ENDIAN
2300 // offset to get the alpha byte; we're always going to
2301 // move by 4 bytes
2302 rowp += 3;
2303 #endif
2304 uint8_t *endrowp = rowp + 4 * width;
2305 while (rowp != endrowp) {
2306 *rowp = 0xff;
2307 rowp += 4;
2310 row += checked_alignedRowSize.value();
2312 } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) {
2313 float* row = static_cast<float*>(data);
2315 for (GLint j = 0; j < height; ++j) {
2316 float* pAlpha = row + 3;
2317 float* pAlphaEnd = pAlpha + 4*width;
2319 while (pAlpha != pAlphaEnd) {
2320 *pAlpha = 1.0f;
2321 pAlpha += 4;
2324 row += checked_alignedRowSize.value();
2326 } else {
2327 NS_WARNING("Unhandled case, how'd we get here?");
2328 return rv.Throw(NS_ERROR_FAILURE);
2334 void
2335 WebGLContext::RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
2337 if (IsContextLost())
2338 return;
2340 if (!mBoundRenderbuffer)
2341 return ErrorInvalidOperation("renderbufferStorage called on renderbuffer 0");
2343 if (target != LOCAL_GL_RENDERBUFFER)
2344 return ErrorInvalidEnumInfo("renderbufferStorage: target", target);
2346 if (width < 0 || height < 0)
2347 return ErrorInvalidValue("renderbufferStorage: width and height must be >= 0");
2349 if (width > mGLMaxRenderbufferSize || height > mGLMaxRenderbufferSize)
2350 return ErrorInvalidValue("renderbufferStorage: width or height exceeds maximum renderbuffer size");
2352 // certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL
2353 GLenum internalformatForGL = internalformat;
2355 switch (internalformat) {
2356 case LOCAL_GL_RGBA4:
2357 case LOCAL_GL_RGB5_A1:
2358 // 16-bit RGBA formats are not supported on desktop GL
2359 if (!gl->IsGLES()) internalformatForGL = LOCAL_GL_RGBA8;
2360 break;
2361 case LOCAL_GL_RGB565:
2362 // the RGB565 format is not supported on desktop GL
2363 if (!gl->IsGLES()) internalformatForGL = LOCAL_GL_RGB8;
2364 break;
2365 case LOCAL_GL_DEPTH_COMPONENT16:
2366 if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24))
2367 internalformatForGL = LOCAL_GL_DEPTH_COMPONENT24;
2368 else if (gl->IsExtensionSupported(gl::GLContext::OES_packed_depth_stencil))
2369 internalformatForGL = LOCAL_GL_DEPTH24_STENCIL8;
2370 break;
2371 case LOCAL_GL_STENCIL_INDEX8:
2372 break;
2373 case LOCAL_GL_DEPTH_STENCIL:
2374 // We emulate this in WebGLRenderbuffer if we don't have the requisite extension.
2375 internalformatForGL = LOCAL_GL_DEPTH24_STENCIL8;
2376 break;
2377 case LOCAL_GL_SRGB8_ALPHA8_EXT:
2378 break;
2379 case LOCAL_GL_RGB16F:
2380 case LOCAL_GL_RGBA16F: {
2381 bool hasExtensions = IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float) &&
2382 IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float);
2383 if (!hasExtensions)
2384 return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", target);
2385 break;
2387 case LOCAL_GL_RGB32F:
2388 case LOCAL_GL_RGBA32F: {
2389 bool hasExtensions = IsExtensionEnabled(WebGLExtensionID::OES_texture_float) &&
2390 IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float);
2391 if (!hasExtensions)
2392 return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", target);
2393 break;
2395 default:
2396 return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", internalformat);
2399 MakeContextCurrent();
2401 bool sizeChanges = width != mBoundRenderbuffer->Width() ||
2402 height != mBoundRenderbuffer->Height() ||
2403 internalformat != mBoundRenderbuffer->InternalFormat();
2404 if (sizeChanges) {
2405 // Invalidate framebuffer status cache
2406 mBoundRenderbuffer->NotifyFBsStatusChanged();
2407 GetAndFlushUnderlyingGLErrors();
2408 mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height);
2409 GLenum error = GetAndFlushUnderlyingGLErrors();
2410 if (error) {
2411 GenerateWarning("renderbufferStorage generated error %s", ErrorName(error));
2412 return;
2414 } else {
2415 mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height);
2418 mBoundRenderbuffer->SetInternalFormat(internalformat);
2419 mBoundRenderbuffer->SetInternalFormatForGL(internalformatForGL);
2420 mBoundRenderbuffer->setDimensions(width, height);
2421 mBoundRenderbuffer->SetImageDataStatus(WebGLImageDataStatus::UninitializedImageData);
2424 void
2425 WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
2427 if (IsContextLost())
2428 return;
2430 if (width < 0 || height < 0)
2431 return ErrorInvalidValue("scissor: negative size");
2433 MakeContextCurrent();
2434 gl->fScissor(x, y, width, height);
2437 void
2438 WebGLContext::StencilFunc(GLenum func, GLint ref, GLuint mask)
2440 if (IsContextLost())
2441 return;
2443 if (!ValidateComparisonEnum(func, "stencilFunc: func"))
2444 return;
2446 mStencilRefFront = ref;
2447 mStencilRefBack = ref;
2448 mStencilValueMaskFront = mask;
2449 mStencilValueMaskBack = mask;
2451 MakeContextCurrent();
2452 gl->fStencilFunc(func, ref, mask);
2455 void
2456 WebGLContext::StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
2458 if (IsContextLost())
2459 return;
2461 if (!ValidateFaceEnum(face, "stencilFuncSeparate: face") ||
2462 !ValidateComparisonEnum(func, "stencilFuncSeparate: func"))
2463 return;
2465 switch (face) {
2466 case LOCAL_GL_FRONT_AND_BACK:
2467 mStencilRefFront = ref;
2468 mStencilRefBack = ref;
2469 mStencilValueMaskFront = mask;
2470 mStencilValueMaskBack = mask;
2471 break;
2472 case LOCAL_GL_FRONT:
2473 mStencilRefFront = ref;
2474 mStencilValueMaskFront = mask;
2475 break;
2476 case LOCAL_GL_BACK:
2477 mStencilRefBack = ref;
2478 mStencilValueMaskBack = mask;
2479 break;
2482 MakeContextCurrent();
2483 gl->fStencilFuncSeparate(face, func, ref, mask);
2486 void
2487 WebGLContext::StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
2489 if (IsContextLost())
2490 return;
2492 if (!ValidateStencilOpEnum(sfail, "stencilOp: sfail") ||
2493 !ValidateStencilOpEnum(dpfail, "stencilOp: dpfail") ||
2494 !ValidateStencilOpEnum(dppass, "stencilOp: dppass"))
2495 return;
2497 MakeContextCurrent();
2498 gl->fStencilOp(sfail, dpfail, dppass);
2501 void
2502 WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
2504 if (IsContextLost())
2505 return;
2507 if (!ValidateFaceEnum(face, "stencilOpSeparate: face") ||
2508 !ValidateStencilOpEnum(sfail, "stencilOpSeparate: sfail") ||
2509 !ValidateStencilOpEnum(dpfail, "stencilOpSeparate: dpfail") ||
2510 !ValidateStencilOpEnum(dppass, "stencilOpSeparate: dppass"))
2511 return;
2513 MakeContextCurrent();
2514 gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
2517 nsresult
2518 WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
2519 RefPtr<DataSourceSurface>& imageOut, WebGLTexelFormat *format)
2521 *format = WebGLTexelFormat::None;
2523 if (!res.mSourceSurface)
2524 return NS_OK;
2525 RefPtr<DataSourceSurface> data = res.mSourceSurface->GetDataSurface();
2526 if (!data) {
2527 // SurfaceFromElement lied!
2528 return NS_OK;
2531 if (!mPixelStorePremultiplyAlpha && res.mIsPremultiplied) {
2532 switch (data->GetFormat()) {
2533 case SurfaceFormat::B8G8R8X8:
2534 // No alpha, so de-facto premult'd.
2535 break;
2536 case SurfaceFormat::B8G8R8A8:
2537 data = gfxUtils::CreateUnpremultipliedDataSurface(data);
2538 break;
2539 default:
2540 MOZ_ASSERT(false, "Format unsupported.");
2541 break;
2545 // We disallow loading cross-domain images and videos that have not been validated
2546 // with CORS as WebGL textures. The reason for doing that is that timing
2547 // attacks on WebGL shaders are able to retrieve approximations of the
2548 // pixel values in WebGL textures; see bug 655987.
2550 // To prevent a loophole where a Canvas2D would be used as a proxy to load
2551 // cross-domain textures, we also disallow loading textures from write-only
2552 // Canvas2D's.
2554 // part 1: check that the DOM element is same-origin, or has otherwise been
2555 // validated for cross-domain use.
2556 if (!res.mCORSUsed) {
2557 bool subsumes;
2558 nsresult rv = mCanvasElement->NodePrincipal()->Subsumes(res.mPrincipal, &subsumes);
2559 if (NS_FAILED(rv) || !subsumes) {
2560 GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. "
2561 "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
2562 return NS_ERROR_DOM_SECURITY_ERR;
2566 // part 2: if the DOM element is write-only, it might contain
2567 // cross-domain image data.
2568 if (res.mIsWriteOnly) {
2569 GenerateWarning("The canvas used as source for texImage2D here is tainted (write-only). It is forbidden "
2570 "to load a WebGL texture from a tainted canvas. A Canvas becomes tainted for example "
2571 "when a cross-domain image is drawn on it. "
2572 "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
2573 return NS_ERROR_DOM_SECURITY_ERR;
2576 // End of security checks, now we should be safe regarding cross-domain images
2577 // Notice that there is never a need to mark the WebGL canvas as write-only, since we reject write-only/cross-domain
2578 // texture sources in the first place.
2580 switch (data->GetFormat()) {
2581 case SurfaceFormat::B8G8R8A8:
2582 *format = WebGLTexelFormat::BGRA8; // careful, our ARGB means BGRA
2583 break;
2584 case SurfaceFormat::B8G8R8X8:
2585 *format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8.
2586 break;
2587 case SurfaceFormat::A8:
2588 *format = WebGLTexelFormat::A8;
2589 break;
2590 case SurfaceFormat::R5G6B5:
2591 *format = WebGLTexelFormat::RGB565;
2592 break;
2593 default:
2594 NS_ASSERTION(false, "Unsupported image format. Unimplemented.");
2595 return NS_ERROR_NOT_IMPLEMENTED;
2598 imageOut = data;
2600 return NS_OK;
2605 void
2606 WebGLContext::Uniform1i(WebGLUniformLocation *location_object, GLint a1)
2608 GLint location;
2609 if (!ValidateUniformSetter("Uniform1i", location_object, location))
2610 return;
2612 // Only uniform1i can take sampler settings.
2613 if (!ValidateSamplerUniformSetter("Uniform1i", location_object, a1))
2614 return;
2616 MakeContextCurrent();
2617 gl->fUniform1i(location, a1);
2620 void
2621 WebGLContext::Uniform2i(WebGLUniformLocation *location_object, GLint a1,
2622 GLint a2)
2624 GLint location;
2625 if (!ValidateUniformSetter("Uniform2i", location_object, location))
2626 return;
2628 MakeContextCurrent();
2629 gl->fUniform2i(location, a1, a2);
2632 void
2633 WebGLContext::Uniform3i(WebGLUniformLocation *location_object, GLint a1,
2634 GLint a2, GLint a3)
2636 GLint location;
2637 if (!ValidateUniformSetter("Uniform3i", location_object, location))
2638 return;
2640 MakeContextCurrent();
2641 gl->fUniform3i(location, a1, a2, a3);
2644 void
2645 WebGLContext::Uniform4i(WebGLUniformLocation *location_object, GLint a1,
2646 GLint a2, GLint a3, GLint a4)
2648 GLint location;
2649 if (!ValidateUniformSetter("Uniform4i", location_object, location))
2650 return;
2652 MakeContextCurrent();
2653 gl->fUniform4i(location, a1, a2, a3, a4);
2656 void
2657 WebGLContext::Uniform1f(WebGLUniformLocation *location_object, GLfloat a1)
2659 GLint location;
2660 if (!ValidateUniformSetter("Uniform1f", location_object, location))
2661 return;
2662 MakeContextCurrent();
2663 gl->fUniform1f(location, a1);
2666 void
2667 WebGLContext::Uniform2f(WebGLUniformLocation *location_object, GLfloat a1,
2668 GLfloat a2)
2670 GLint location;
2671 if (!ValidateUniformSetter("Uniform2f", location_object, location))
2672 return;
2673 MakeContextCurrent();
2674 gl->fUniform2f(location, a1, a2);
2677 void
2678 WebGLContext::Uniform3f(WebGLUniformLocation *location_object, GLfloat a1,
2679 GLfloat a2, GLfloat a3)
2681 GLint location;
2682 if (!ValidateUniformSetter("Uniform3f", location_object, location))
2683 return;
2684 MakeContextCurrent();
2685 gl->fUniform3f(location, a1, a2, a3);
2688 void
2689 WebGLContext::Uniform4f(WebGLUniformLocation *location_object, GLfloat a1,
2690 GLfloat a2, GLfloat a3, GLfloat a4)
2692 GLint location;
2693 if (!ValidateUniformSetter("Uniform4f", location_object, location))
2694 return;
2695 MakeContextCurrent();
2696 gl->fUniform4f(location, a1, a2, a3, a4);
2699 void
2700 WebGLContext::Uniform1iv_base(WebGLUniformLocation *location_object,
2701 uint32_t arrayLength, const GLint* data)
2703 uint32_t numElementsToUpload;
2704 GLint location;
2705 if (!ValidateUniformArraySetter("Uniform1iv", 1, location_object, location,
2706 numElementsToUpload, arrayLength)) {
2707 return;
2710 if (!ValidateSamplerUniformSetter("Uniform1iv", location_object, data[0]))
2711 return;
2713 MakeContextCurrent();
2714 gl->fUniform1iv(location, numElementsToUpload, data);
2717 void
2718 WebGLContext::Uniform2iv_base(WebGLUniformLocation *location_object,
2719 uint32_t arrayLength, const GLint* data)
2721 uint32_t numElementsToUpload;
2722 GLint location;
2723 if (!ValidateUniformArraySetter("Uniform2iv", 2, location_object, location,
2724 numElementsToUpload, arrayLength)) {
2725 return;
2728 if (!ValidateSamplerUniformSetter("Uniform2iv", location_object, data[0]) ||
2729 !ValidateSamplerUniformSetter("Uniform2iv", location_object, data[1]))
2731 return;
2734 MakeContextCurrent();
2735 gl->fUniform2iv(location, numElementsToUpload, data);
2738 void
2739 WebGLContext::Uniform3iv_base(WebGLUniformLocation *location_object,
2740 uint32_t arrayLength, const GLint* data)
2742 uint32_t numElementsToUpload;
2743 GLint location;
2744 if (!ValidateUniformArraySetter("Uniform3iv", 3, location_object, location,
2745 numElementsToUpload, arrayLength)) {
2746 return;
2749 if (!ValidateSamplerUniformSetter("Uniform3iv", location_object, data[0]) ||
2750 !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[1]) ||
2751 !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[2]))
2753 return;
2756 MakeContextCurrent();
2757 gl->fUniform3iv(location, numElementsToUpload, data);
2760 void
2761 WebGLContext::Uniform4iv_base(WebGLUniformLocation *location_object,
2762 uint32_t arrayLength, const GLint* data)
2764 uint32_t numElementsToUpload;
2765 GLint location;
2766 if (!ValidateUniformArraySetter("Uniform4iv", 4, location_object, location,
2767 numElementsToUpload, arrayLength)) {
2768 return;
2771 if (!ValidateSamplerUniformSetter("Uniform4iv", location_object, data[0]) ||
2772 !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[1]) ||
2773 !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[2]) ||
2774 !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[3]))
2776 return;
2779 MakeContextCurrent();
2780 gl->fUniform4iv(location, numElementsToUpload, data);
2783 void
2784 WebGLContext::Uniform1fv_base(WebGLUniformLocation *location_object,
2785 uint32_t arrayLength, const GLfloat* data)
2787 uint32_t numElementsToUpload;
2788 GLint location;
2789 if (!ValidateUniformArraySetter("Uniform1fv", 1, location_object, location,
2790 numElementsToUpload, arrayLength)) {
2791 return;
2793 MakeContextCurrent();
2794 gl->fUniform1fv(location, numElementsToUpload, data);
2797 void
2798 WebGLContext::Uniform2fv_base(WebGLUniformLocation *location_object,
2799 uint32_t arrayLength, const GLfloat* data)
2801 uint32_t numElementsToUpload;
2802 GLint location;
2803 if (!ValidateUniformArraySetter("Uniform2fv", 2, location_object, location,
2804 numElementsToUpload, arrayLength)) {
2805 return;
2807 MakeContextCurrent();
2808 gl->fUniform2fv(location, numElementsToUpload, data);
2811 void
2812 WebGLContext::Uniform3fv_base(WebGLUniformLocation *location_object,
2813 uint32_t arrayLength, const GLfloat* data)
2815 uint32_t numElementsToUpload;
2816 GLint location;
2817 if (!ValidateUniformArraySetter("Uniform3fv", 3, location_object, location,
2818 numElementsToUpload, arrayLength)) {
2819 return;
2821 MakeContextCurrent();
2822 gl->fUniform3fv(location, numElementsToUpload, data);
2825 void
2826 WebGLContext::Uniform4fv_base(WebGLUniformLocation *location_object,
2827 uint32_t arrayLength, const GLfloat* data)
2829 uint32_t numElementsToUpload;
2830 GLint location;
2831 if (!ValidateUniformArraySetter("Uniform4fv", 4, location_object, location,
2832 numElementsToUpload, arrayLength)) {
2833 return;
2835 MakeContextCurrent();
2836 gl->fUniform4fv(location, numElementsToUpload, data);
2839 void
2840 WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* location_object,
2841 WebGLboolean aTranspose, uint32_t arrayLength,
2842 const float* data)
2844 uint32_t numElementsToUpload;
2845 GLint location;
2846 if (!ValidateUniformMatrixArraySetter("UniformMatrix2fv", 2, location_object, location,
2847 numElementsToUpload, arrayLength, aTranspose)) {
2848 return;
2850 MakeContextCurrent();
2851 gl->fUniformMatrix2fv(location, numElementsToUpload, false, data);
2854 void
2855 WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* location_object,
2856 WebGLboolean aTranspose, uint32_t arrayLength,
2857 const float* data)
2859 uint32_t numElementsToUpload;
2860 GLint location;
2861 if (!ValidateUniformMatrixArraySetter("UniformMatrix3fv", 3, location_object, location,
2862 numElementsToUpload, arrayLength, aTranspose)) {
2863 return;
2865 MakeContextCurrent();
2866 gl->fUniformMatrix3fv(location, numElementsToUpload, false, data);
2869 void
2870 WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* location_object,
2871 WebGLboolean aTranspose, uint32_t arrayLength,
2872 const float* data)
2874 uint32_t numElementsToUpload;
2875 GLint location;
2876 if (!ValidateUniformMatrixArraySetter("UniformMatrix4fv", 4, location_object, location,
2877 numElementsToUpload, arrayLength, aTranspose)) {
2878 return;
2880 MakeContextCurrent();
2881 gl->fUniformMatrix4fv(location, numElementsToUpload, false, data);
2884 void
2885 WebGLContext::UseProgram(WebGLProgram *prog)
2887 if (IsContextLost())
2888 return;
2890 if (!ValidateObjectAllowNull("useProgram", prog))
2891 return;
2893 MakeContextCurrent();
2895 InvalidateBufferFetching();
2897 GLuint progname = prog ? prog->GLName() : 0;
2899 if (prog && !prog->LinkStatus())
2900 return ErrorInvalidOperation("useProgram: program was not linked successfully");
2902 gl->fUseProgram(progname);
2904 mCurrentProgram = prog;
2907 void
2908 WebGLContext::ValidateProgram(WebGLProgram *prog)
2910 if (IsContextLost())
2911 return;
2913 if (!ValidateObject("validateProgram", prog))
2914 return;
2916 MakeContextCurrent();
2918 #ifdef XP_MACOSX
2919 // see bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed with Mac OS 10.6.7
2920 if (gl->WorkAroundDriverBugs()) {
2921 GenerateWarning("validateProgram: implemented as a no-operation on Mac to work around crashes");
2922 return;
2924 #endif
2926 GLuint progname = prog->GLName();
2927 gl->fValidateProgram(progname);
2930 already_AddRefed<WebGLFramebuffer>
2931 WebGLContext::CreateFramebuffer()
2933 if (IsContextLost())
2934 return nullptr;
2935 nsRefPtr<WebGLFramebuffer> globj = new WebGLFramebuffer(this);
2936 return globj.forget();
2939 already_AddRefed<WebGLRenderbuffer>
2940 WebGLContext::CreateRenderbuffer()
2942 if (IsContextLost())
2943 return nullptr;
2944 nsRefPtr<WebGLRenderbuffer> globj = new WebGLRenderbuffer(this);
2945 return globj.forget();
2948 void
2949 WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
2951 if (IsContextLost())
2952 return;
2954 if (width < 0 || height < 0)
2955 return ErrorInvalidValue("viewport: negative size");
2957 MakeContextCurrent();
2958 gl->fViewport(x, y, width, height);
2960 mViewportX = x;
2961 mViewportY = y;
2962 mViewportWidth = width;
2963 mViewportHeight = height;
2966 void
2967 WebGLContext::CompileShader(WebGLShader *shader)
2969 if (IsContextLost())
2970 return;
2972 if (!ValidateObject("compileShader", shader))
2973 return;
2975 GLuint shadername = shader->GLName();
2977 shader->SetCompileStatus(false);
2979 // nothing to do if the validator is disabled
2980 if (!mShaderValidation)
2981 return;
2983 // nothing to do if translation was already done
2984 if (!shader->NeedsTranslation())
2985 return;
2987 MakeContextCurrent();
2989 ShShaderOutput targetShaderSourceLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT;
2991 ShHandle compiler = 0;
2992 ShBuiltInResources resources;
2994 memset(&resources, 0, sizeof(ShBuiltInResources));
2996 ShInitBuiltInResources(&resources);
2998 resources.MaxVertexAttribs = mGLMaxVertexAttribs;
2999 resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
3000 resources.MaxVaryingVectors = mGLMaxVaryingVectors;
3001 resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
3002 resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits;
3003 resources.MaxTextureImageUnits = mGLMaxTextureImageUnits;
3004 resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
3005 resources.MaxDrawBuffers = mGLMaxDrawBuffers;
3007 if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
3008 resources.EXT_frag_depth = 1;
3010 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
3011 resources.OES_standard_derivatives = 1;
3013 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
3014 resources.EXT_draw_buffers = 1;
3016 if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod))
3017 resources.EXT_shader_texture_lod = 1;
3019 // Tell ANGLE to allow highp in frag shaders. (unless disabled)
3020 // If underlying GLES doesn't have highp in frag shaders, it should complain anyways.
3021 resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
3023 resources.HashFunction = WebGLProgram::IdentifierHashFunction;
3025 if (gl->WorkAroundDriverBugs()) {
3026 #ifdef XP_MACOSX
3027 if (gl->Vendor() == gl::GLVendor::NVIDIA) {
3028 // Work around bug 890432
3029 resources.MaxExpressionComplexity = 1000;
3031 #endif
3034 // We're storing an actual instance of StripComments because, if we don't, the
3035 // cleanSource nsAString instance will be destroyed before the reference is
3036 // actually used.
3037 StripComments stripComments(shader->Source());
3038 const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length());
3039 if (!ValidateGLSLString(cleanSource, "compileShader"))
3040 return;
3042 // shaderSource() already checks that the source stripped of comments is in the
3043 // 7-bit ASCII range, so we can skip the NS_IsAscii() check.
3044 NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
3046 if (gl->WorkAroundDriverBugs()) {
3047 const uint32_t maxSourceLength = 0x3ffff;
3048 if (sourceCString.Length() > maxSourceLength)
3049 return ErrorInvalidValue("compileShader: source has more than %d characters",
3050 maxSourceLength);
3053 const char *s = sourceCString.get();
3055 #define WEBGL2_BYPASS_ANGLE
3056 #ifdef WEBGL2_BYPASS_ANGLE
3058 * The bypass don't bring a full support for GLSL ES 3.0, but the main purpose
3059 * is to natively bring gl_InstanceID (to do instanced rendering) and gl_FragData
3061 * To remove the bypass code, just comment #define WEBGL2_BYPASS_ANGLE above
3063 * To bypass angle, the context must be a WebGL 2 and the shader must have the
3064 * following line at the very top :
3065 * #version proto-200
3067 * In this case, byPassANGLE == true and here is what we do :
3068 * We create two shader source code:
3069 * - one for the driver, that enable GL_EXT_gpu_shader4
3070 * - one for the angle compilor, to get informations about vertex attributes
3071 * and uniforms
3073 static const char *bypassPrefixSearch = "#version proto-200";
3074 static const char *bypassANGLEPrefix[2] = {"precision mediump float;\n"
3075 "#define gl_VertexID 0\n"
3076 "#define gl_InstanceID 0\n",
3078 "precision mediump float;\n"
3079 "#extension GL_EXT_draw_buffers : enable\n"
3080 "#define gl_PrimitiveID 0\n"};
3082 const bool bypassANGLE = IsWebGL2() && (strstr(s, bypassPrefixSearch) != 0);
3084 const char *angleShaderCode = s;
3085 nsTArray<char> bypassANGLEShaderCode;
3086 nsTArray<char> bypassDriverShaderCode;
3088 if (bypassANGLE) {
3089 const int bypassStage = (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) ? 1 : 0;
3090 const char *originalShader = strstr(s, bypassPrefixSearch) + strlen(bypassPrefixSearch);
3091 int originalShaderSize = strlen(s) - (originalShader - s);
3092 int bypassShaderCodeSize = originalShaderSize + 4096 + 1;
3094 bypassANGLEShaderCode.SetLength(bypassShaderCodeSize);
3095 strcpy(bypassANGLEShaderCode.Elements(), bypassANGLEPrefix[bypassStage]);
3096 strcat(bypassANGLEShaderCode.Elements(), originalShader);
3098 bypassDriverShaderCode.SetLength(bypassShaderCodeSize);
3099 strcpy(bypassDriverShaderCode.Elements(), "#extension GL_EXT_gpu_shader4 : enable\n");
3100 strcat(bypassDriverShaderCode.Elements(), originalShader);
3102 angleShaderCode = bypassANGLEShaderCode.Elements();
3104 #endif
3106 compiler = ShConstructCompiler((ShShaderType) shader->ShaderType(),
3107 SH_WEBGL_SPEC,
3108 targetShaderSourceLanguage,
3109 &resources);
3111 int compileOptions = SH_VARIABLES |
3112 SH_ENFORCE_PACKING_RESTRICTIONS |
3113 SH_INIT_VARYINGS_WITHOUT_STATIC_USE |
3114 SH_OBJECT_CODE;
3116 if (resources.MaxExpressionComplexity > 0) {
3117 compileOptions |= SH_LIMIT_EXPRESSION_COMPLEXITY;
3120 #ifndef XP_MACOSX
3121 // We want to do this everywhere, but to do this on Mac, we need
3122 // to do it only on Mac OSX > 10.6 as this causes the shader
3123 // compiler in 10.6 to crash
3124 compileOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
3125 #endif
3127 #ifdef XP_MACOSX
3128 if (gl->WorkAroundDriverBugs()) {
3129 // Work around bug 665578 and bug 769810
3130 if (gl->Vendor() == gl::GLVendor::ATI) {
3131 compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
3134 // Work around bug 735560
3135 if (gl->Vendor() == gl::GLVendor::Intel) {
3136 compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
3139 // Work around bug 636926
3140 if (gl->Vendor() == gl::GLVendor::NVIDIA) {
3141 compileOptions |= SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
3144 // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
3145 // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
3146 compileOptions |= SH_UNFOLD_SHORT_CIRCUIT;
3148 #endif
3150 #ifdef WEBGL2_BYPASS_ANGLE
3151 if (!ShCompile(compiler, &angleShaderCode, 1, compileOptions)) {
3152 #else
3153 if (!ShCompile(compiler, &s, 1, compileOptions)) {
3154 #endif
3155 size_t lenWithNull = 0;
3156 ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &lenWithNull);
3158 if (!lenWithNull) {
3159 // Error in ShGetInfo.
3160 shader->SetTranslationFailure(NS_LITERAL_CSTRING("Internal error: failed to get shader info log"));
3161 } else {
3162 size_t len = lenWithNull - 1;
3164 nsAutoCString info;
3165 if (len) {
3166 // Don't allocate or try to write to zero length string
3167 info.SetLength(len); // Allocates len+1, for the null-term.
3168 ShGetInfoLog(compiler, info.BeginWriting());
3170 shader->SetTranslationFailure(info);
3172 ShDestruct(compiler);
3173 shader->SetCompileStatus(false);
3174 return;
3177 size_t num_attributes = 0;
3178 ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTES, &num_attributes);
3179 size_t num_uniforms = 0;
3180 ShGetInfo(compiler, SH_ACTIVE_UNIFORMS, &num_uniforms);
3181 size_t attrib_max_length = 0;
3182 ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attrib_max_length);
3183 size_t uniform_max_length = 0;
3184 ShGetInfo(compiler, SH_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_length);
3185 size_t mapped_max_length = 0;
3186 ShGetInfo(compiler, SH_MAPPED_NAME_MAX_LENGTH, &mapped_max_length);
3188 shader->mAttribMaxNameLength = attrib_max_length;
3190 shader->mAttributes.Clear();
3191 shader->mUniforms.Clear();
3192 shader->mUniformInfos.Clear();
3194 nsAutoArrayPtr<char> attribute_name(new char[attrib_max_length+1]);
3195 nsAutoArrayPtr<char> uniform_name(new char[uniform_max_length+1]);
3196 nsAutoArrayPtr<char> mapped_name(new char[mapped_max_length+1]);
3198 for (size_t i = 0; i < num_uniforms; i++) {
3199 size_t length;
3200 int size;
3201 ShDataType type;
3202 ShPrecisionType precision;
3203 int staticUse;
3204 ShGetVariableInfo(compiler, SH_ACTIVE_UNIFORMS, (int)i,
3205 &length, &size, &type,
3206 &precision, &staticUse,
3207 uniform_name,
3208 mapped_name);
3210 shader->mUniforms.AppendElement(WebGLMappedIdentifier(
3211 nsDependentCString(uniform_name),
3212 nsDependentCString(mapped_name)));
3214 // we need uniform info to validate uniform setter calls
3215 char mappedNameLength = strlen(mapped_name);
3216 char mappedNameLastChar = mappedNameLength > 1
3217 ? mapped_name[mappedNameLength - 1]
3218 : 0;
3219 shader->mUniformInfos.AppendElement(WebGLUniformInfo(
3220 size,
3221 mappedNameLastChar == ']',
3222 type));
3225 for (size_t i = 0; i < num_attributes; i++) {
3226 size_t length;
3227 int size;
3228 ShDataType type;
3229 ShPrecisionType precision;
3230 int staticUse;
3231 ShGetVariableInfo(compiler, SH_ACTIVE_ATTRIBUTES, (int)i,
3232 &length, &size, &type,
3233 &precision, &staticUse,
3234 attribute_name,
3235 mapped_name);
3236 shader->mAttributes.AppendElement(WebGLMappedIdentifier(
3237 nsDependentCString(attribute_name),
3238 nsDependentCString(mapped_name)));
3241 size_t lenWithNull = 0;
3242 ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &lenWithNull);
3243 MOZ_ASSERT(lenWithNull >= 1);
3244 size_t len = lenWithNull - 1;
3246 nsAutoCString translatedSrc;
3247 translatedSrc.SetLength(len); // Allocates len+1, for the null-term.
3248 ShGetObjectCode(compiler, translatedSrc.BeginWriting());
3250 CopyASCIItoUTF16(translatedSrc, shader->mTranslatedSource);
3252 const char *ts = translatedSrc.get();
3254 #ifdef WEBGL2_BYPASS_ANGLE
3255 if (bypassANGLE) {
3256 const char* driverShaderCode = bypassDriverShaderCode.Elements();
3257 gl->fShaderSource(shadername, 1, (const GLchar**) &driverShaderCode, nullptr);
3258 } else {
3259 gl->fShaderSource(shadername, 1, &ts, nullptr);
3261 #else
3262 gl->fShaderSource(shadername, 1, &ts, nullptr);
3263 #endif
3265 shader->SetTranslationSuccess();
3267 ShDestruct(compiler);
3269 gl->fCompileShader(shadername);
3270 GLint ok;
3271 gl->fGetShaderiv(shadername, LOCAL_GL_COMPILE_STATUS, &ok);
3272 shader->SetCompileStatus(ok);
3275 void
3276 WebGLContext::CompressedTexImage2D(GLenum target, GLint level, GLenum internalformat,
3277 GLsizei width, GLsizei height, GLint border,
3278 const ArrayBufferView& view)
3280 if (IsContextLost())
3281 return;
3283 const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage;
3285 if (!ValidateTexImage(2, target, level, internalformat,
3286 0, 0, 0, width, height, 0,
3287 border, internalformat, LOCAL_GL_UNSIGNED_BYTE,
3288 func))
3290 return;
3293 view.ComputeLengthAndData();
3295 uint32_t byteLength = view.Length();
3296 if (!ValidateCompTexImageDataSize(target, internalformat, width, height, byteLength, func)) {
3297 return;
3300 if (!ValidateCompTexImageSize(target, level, internalformat, 0, 0,
3301 width, height, width, height, func))
3303 return;
3306 MakeContextCurrent();
3307 gl->fCompressedTexImage2D(target, level, internalformat, width, height, border, byteLength, view.Data());
3308 WebGLTexture* tex = activeBoundTextureForTarget(target);
3309 MOZ_ASSERT(tex);
3310 tex->SetImageInfo(target, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE,
3311 WebGLImageDataStatus::InitializedImageData);
3314 void
3315 WebGLContext::CompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset,
3316 GLint yoffset, GLsizei width, GLsizei height,
3317 GLenum format, const ArrayBufferView& view)
3319 if (IsContextLost())
3320 return;
3322 const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage;
3324 if (!ValidateTexImage(2, target,
3325 level, format,
3326 xoffset, yoffset, 0,
3327 width, height, 0,
3328 0, format, LOCAL_GL_UNSIGNED_BYTE,
3329 func))
3331 return;
3334 WebGLTexture *tex = activeBoundTextureForTarget(target);
3335 MOZ_ASSERT(tex);
3336 WebGLTexture::ImageInfo& levelInfo = tex->ImageInfoAt(target, level);
3338 view.ComputeLengthAndData();
3340 uint32_t byteLength = view.Length();
3341 if (!ValidateCompTexImageDataSize(target, format, width, height, byteLength, func))
3342 return;
3344 if (!ValidateCompTexImageSize(target, level, format,
3345 xoffset, yoffset,
3346 width, height,
3347 levelInfo.Width(), levelInfo.Height(),
3348 func))
3350 return;
3353 if (levelInfo.HasUninitializedImageData())
3354 tex->DoDeferredImageInitialization(target, level);
3356 MakeContextCurrent();
3357 gl->fCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, byteLength, view.Data());
3360 JS::Value
3361 WebGLContext::GetShaderParameter(WebGLShader *shader, GLenum pname)
3363 if (IsContextLost())
3364 return JS::NullValue();
3366 if (!ValidateObject("getShaderParameter: shader", shader))
3367 return JS::NullValue();
3369 GLuint shadername = shader->GLName();
3371 MakeContextCurrent();
3373 switch (pname) {
3374 case LOCAL_GL_SHADER_TYPE:
3376 GLint i = 0;
3377 gl->fGetShaderiv(shadername, pname, &i);
3378 return JS::NumberValue(uint32_t(i));
3380 break;
3381 case LOCAL_GL_DELETE_STATUS:
3382 return JS::BooleanValue(shader->IsDeleteRequested());
3383 break;
3384 case LOCAL_GL_COMPILE_STATUS:
3386 GLint i = 0;
3387 gl->fGetShaderiv(shadername, pname, &i);
3388 return JS::BooleanValue(bool(i));
3390 break;
3391 default:
3392 ErrorInvalidEnumInfo("getShaderParameter: parameter", pname);
3395 return JS::NullValue();
3398 void
3399 WebGLContext::GetShaderInfoLog(WebGLShader *shader, nsAString& retval)
3401 nsAutoCString s;
3402 GetShaderInfoLog(shader, s);
3403 if (s.IsVoid())
3404 retval.SetIsVoid(true);
3405 else
3406 CopyASCIItoUTF16(s, retval);
3409 void
3410 WebGLContext::GetShaderInfoLog(WebGLShader *shader, nsACString& retval)
3412 if (IsContextLost())
3414 retval.SetIsVoid(true);
3415 return;
3418 if (!ValidateObject("getShaderInfoLog: shader", shader))
3419 return;
3421 retval = shader->TranslationLog();
3422 if (!retval.IsVoid()) {
3423 return;
3426 MakeContextCurrent();
3428 GLuint shadername = shader->GLName();
3429 GLint k = -1;
3430 gl->fGetShaderiv(shadername, LOCAL_GL_INFO_LOG_LENGTH, &k);
3431 if (k == -1) {
3432 // XXX GL Error? should never happen.
3433 return;
3436 if (k == 0) {
3437 retval.Truncate();
3438 return;
3441 retval.SetCapacity(k);
3442 gl->fGetShaderInfoLog(shadername, k, &k, (char*) retval.BeginWriting());
3443 retval.SetLength(k);
3446 already_AddRefed<WebGLShaderPrecisionFormat>
3447 WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
3449 if (IsContextLost())
3450 return nullptr;
3452 switch (shadertype) {
3453 case LOCAL_GL_FRAGMENT_SHADER:
3454 case LOCAL_GL_VERTEX_SHADER:
3455 break;
3456 default:
3457 ErrorInvalidEnumInfo("getShaderPrecisionFormat: shadertype", shadertype);
3458 return nullptr;
3461 switch (precisiontype) {
3462 case LOCAL_GL_LOW_FLOAT:
3463 case LOCAL_GL_MEDIUM_FLOAT:
3464 case LOCAL_GL_HIGH_FLOAT:
3465 case LOCAL_GL_LOW_INT:
3466 case LOCAL_GL_MEDIUM_INT:
3467 case LOCAL_GL_HIGH_INT:
3468 break;
3469 default:
3470 ErrorInvalidEnumInfo("getShaderPrecisionFormat: precisiontype", precisiontype);
3471 return nullptr;
3474 MakeContextCurrent();
3475 GLint range[2], precision;
3477 if (mDisableFragHighP &&
3478 shadertype == LOCAL_GL_FRAGMENT_SHADER &&
3479 (precisiontype == LOCAL_GL_HIGH_FLOAT ||
3480 precisiontype == LOCAL_GL_HIGH_INT))
3482 precision = 0;
3483 range[0] = 0;
3484 range[1] = 0;
3485 } else {
3486 gl->fGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision);
3489 nsRefPtr<WebGLShaderPrecisionFormat> retShaderPrecisionFormat
3490 = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision);
3491 return retShaderPrecisionFormat.forget();
3494 void
3495 WebGLContext::GetShaderSource(WebGLShader *shader, nsAString& retval)
3497 if (IsContextLost()) {
3498 retval.SetIsVoid(true);
3499 return;
3502 if (!ValidateObject("getShaderSource: shader", shader))
3503 return;
3505 retval.Assign(shader->Source());
3508 void
3509 WebGLContext::ShaderSource(WebGLShader *shader, const nsAString& source)
3511 if (IsContextLost())
3512 return;
3514 if (!ValidateObject("shaderSource: shader", shader))
3515 return;
3517 // We're storing an actual instance of StripComments because, if we don't, the
3518 // cleanSource nsAString instance will be destroyed before the reference is
3519 // actually used.
3520 StripComments stripComments(source);
3521 const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length());
3522 if (!ValidateGLSLString(cleanSource, "compileShader"))
3523 return;
3525 shader->SetSource(source);
3527 shader->SetNeedsTranslation();
3530 void
3531 WebGLContext::GetShaderTranslatedSource(WebGLShader *shader, nsAString& retval)
3533 if (IsContextLost()) {
3534 retval.SetIsVoid(true);
3535 return;
3538 if (!ValidateObject("getShaderTranslatedSource: shader", shader))
3539 return;
3541 retval.Assign(shader->TranslatedSource());
3544 GLenum WebGLContext::CheckedTexImage2D(GLenum target,
3545 GLint level,
3546 GLenum internalFormat,
3547 GLsizei width,
3548 GLsizei height,
3549 GLint border,
3550 GLenum format,
3551 GLenum type,
3552 const GLvoid *data)
3554 MOZ_ASSERT(internalFormat == format);
3555 WebGLTexture *tex = activeBoundTextureForTarget(target);
3556 MOZ_ASSERT(tex != nullptr, "no texture bound");
3558 bool sizeMayChange = true;
3560 if (tex->HasImageInfoAt(target, level)) {
3561 const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
3562 sizeMayChange = width != imageInfo.Width() ||
3563 height != imageInfo.Height() ||
3564 format != imageInfo.WebGLFormat() ||
3565 type != imageInfo.WebGLType();
3568 // Convert to format and type required by OpenGL 'driver'.
3569 GLenum driverType = DriverTypeFromType(gl, type);
3570 GLenum driverInternalFormat = LOCAL_GL_NONE;
3571 GLenum driverFormat = LOCAL_GL_NONE;
3572 DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
3574 if (sizeMayChange) {
3575 GetAndFlushUnderlyingGLErrors();
3578 gl->fTexImage2D(target, level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
3580 GLenum error = LOCAL_GL_NO_ERROR;
3581 if (sizeMayChange) {
3582 error = GetAndFlushUnderlyingGLErrors();
3585 return error;
3588 void
3589 WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat,
3590 GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
3591 GLint border,
3592 GLenum format, GLenum type,
3593 void* data, uint32_t byteLength,
3594 int jsArrayType, // a TypedArray format enum, or -1 if not relevant
3595 WebGLTexelFormat srcFormat, bool srcPremultiplied)
3597 const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
3599 if (!ValidateTexImage(2, target, level, internalformat,
3600 0, 0, 0,
3601 width, height, 0,
3602 border, format, type, func))
3604 return;
3607 const bool isDepthTexture = format == LOCAL_GL_DEPTH_COMPONENT ||
3608 format == LOCAL_GL_DEPTH_STENCIL;
3610 if (isDepthTexture) {
3611 if (data != nullptr || level != 0)
3612 return ErrorInvalidOperation("texImage2D: "
3613 "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
3614 "data must be nullptr, "
3615 "level must be zero");
3618 if (!ValidateTexInputData(type, jsArrayType, func))
3619 return;
3621 WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
3622 WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
3624 uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
3626 CheckedUint32 checked_neededByteLength =
3627 GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
3629 CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
3630 CheckedUint32 checked_alignedRowSize =
3631 RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
3633 if (!checked_neededByteLength.isValid())
3634 return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
3636 uint32_t bytesNeeded = checked_neededByteLength.value();
3638 if (byteLength && byteLength < bytesNeeded)
3639 return ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)",
3640 bytesNeeded, byteLength);
3642 WebGLTexture *tex = activeBoundTextureForTarget(target);
3644 if (!tex)
3645 return ErrorInvalidOperation("texImage2D: no texture is bound to this target");
3647 MakeContextCurrent();
3649 nsAutoArrayPtr<uint8_t> convertedData;
3650 void* pixels = nullptr;
3651 WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
3653 if (byteLength) {
3654 size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
3655 uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
3656 size_t dstPlainRowSize = dstTexelSize * width;
3657 size_t unpackAlignment = mPixelStoreUnpackAlignment;
3658 size_t dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
3660 if (actualSrcFormat == dstFormat &&
3661 srcPremultiplied == mPixelStorePremultiplyAlpha &&
3662 srcStride == dstStride &&
3663 !mPixelStoreFlipY)
3665 // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
3666 pixels = data;
3668 else
3670 size_t convertedDataSize = height * dstStride;
3671 convertedData = new ((fallible_t())) uint8_t[convertedDataSize];
3672 if (!convertedData) {
3673 ErrorOutOfMemory("texImage2D: Ran out of memory when allocating"
3674 " a buffer for doing format conversion.");
3675 return;
3677 ConvertImage(width, height, srcStride, dstStride,
3678 static_cast<uint8_t*>(data), convertedData,
3679 actualSrcFormat, srcPremultiplied,
3680 dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
3681 pixels = reinterpret_cast<void*>(convertedData.get());
3683 imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData;
3686 GLenum error = CheckedTexImage2D(target, level, internalformat, width,
3687 height, border, format, type, pixels);
3689 if (error) {
3690 GenerateWarning("texImage2D generated error %s", ErrorName(error));
3691 return;
3694 // in all of the code paths above, we should have either initialized data,
3695 // or allocated data and left it uninitialized, but in any case we shouldn't
3696 // have NoImageData at this point.
3697 MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData);
3699 tex->SetImageInfo(target, level, width, height, format, type, imageInfoStatusIfSuccess);
3702 void
3703 WebGLContext::TexImage2D(GLenum target, GLint level,
3704 GLenum internalformat, GLsizei width,
3705 GLsizei height, GLint border, GLenum format,
3706 GLenum type, const Nullable<ArrayBufferView> &pixels, ErrorResult& rv)
3708 if (IsContextLost())
3709 return;
3711 void* data;
3712 uint32_t length;
3713 int jsArrayType;
3714 if (pixels.IsNull()) {
3715 data = nullptr;
3716 length = 0;
3717 jsArrayType = -1;
3718 } else {
3719 const ArrayBufferView& view = pixels.Value();
3720 view.ComputeLengthAndData();
3722 data = view.Data();
3723 length = view.Length();
3724 jsArrayType = int(JS_GetArrayBufferViewType(view.Obj()));
3727 return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type,
3728 data, length, jsArrayType,
3729 WebGLTexelFormat::Auto, false);
3732 void
3733 WebGLContext::TexImage2D(GLenum target, GLint level,
3734 GLenum internalformat, GLenum format,
3735 GLenum type, ImageData* pixels, ErrorResult& rv)
3737 if (IsContextLost())
3738 return;
3740 if (!pixels) {
3741 // Spec says to generate an INVALID_VALUE error
3742 return ErrorInvalidValue("texImage2D: null ImageData");
3745 Uint8ClampedArray arr;
3746 DebugOnly<bool> inited = arr.Init(pixels->GetDataObject());
3747 MOZ_ASSERT(inited);
3748 arr.ComputeLengthAndData();
3750 return TexImage2D_base(target, level, internalformat, pixels->Width(),
3751 pixels->Height(), 4*pixels->Width(), 0,
3752 format, type, arr.Data(), arr.Length(), -1,
3753 WebGLTexelFormat::RGBA8, false);
3757 void
3758 WebGLContext::TexSubImage2D_base(GLenum target, GLint level,
3759 GLint xoffset, GLint yoffset,
3760 GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
3761 GLenum format, GLenum type,
3762 void* data, uint32_t byteLength,
3763 int jsArrayType,
3764 WebGLTexelFormat srcFormat, bool srcPremultiplied)
3766 const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
3768 if (!ValidateTexImage(2, target, level, format,
3769 xoffset, yoffset, 0,
3770 width, height, 0,
3771 0, format, type, func))
3773 return;
3776 if (!ValidateTexInputData(type, jsArrayType, func))
3777 return;
3779 WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
3780 WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
3782 uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
3784 if (width == 0 || height == 0)
3785 return; // ES 2.0 says it has no effect, we better return right now
3787 CheckedUint32 checked_neededByteLength =
3788 GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
3790 CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
3792 CheckedUint32 checked_alignedRowSize =
3793 RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
3795 if (!checked_neededByteLength.isValid())
3796 return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
3798 uint32_t bytesNeeded = checked_neededByteLength.value();
3800 if (byteLength < bytesNeeded)
3801 return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
3803 WebGLTexture *tex = activeBoundTextureForTarget(target);
3804 const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
3806 if (imageInfo.HasUninitializedImageData())
3807 tex->DoDeferredImageInitialization(target, level);
3809 MakeContextCurrent();
3811 size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
3812 uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
3813 size_t dstPlainRowSize = dstTexelSize * width;
3814 // There are checks above to ensure that this won't overflow.
3815 size_t dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
3817 void* pixels = data;
3818 nsAutoArrayPtr<uint8_t> convertedData;
3820 // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
3821 bool noConversion = (actualSrcFormat == dstFormat &&
3822 srcPremultiplied == mPixelStorePremultiplyAlpha &&
3823 srcStride == dstStride &&
3824 !mPixelStoreFlipY);
3826 if (!noConversion) {
3827 size_t convertedDataSize = height * dstStride;
3828 convertedData = new ((fallible_t())) uint8_t[convertedDataSize];
3829 if (!convertedData) {
3830 ErrorOutOfMemory("texImage2D: Ran out of memory when allocating"
3831 " a buffer for doing format conversion.");
3832 return;
3834 ConvertImage(width, height, srcStride, dstStride,
3835 static_cast<const uint8_t*>(data), convertedData,
3836 actualSrcFormat, srcPremultiplied,
3837 dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
3838 pixels = reinterpret_cast<void*>(convertedData.get());
3841 GLenum driverType = DriverTypeFromType(gl, type);
3842 GLenum driverInternalFormat = LOCAL_GL_NONE;
3843 GLenum driverFormat = LOCAL_GL_NONE;
3844 DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
3846 gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, driverFormat, driverType, pixels);
3849 void
3850 WebGLContext::TexSubImage2D(GLenum target, GLint level,
3851 GLint xoffset, GLint yoffset,
3852 GLsizei width, GLsizei height,
3853 GLenum format, GLenum type,
3854 const Nullable<ArrayBufferView> &pixels,
3855 ErrorResult& rv)
3857 if (IsContextLost())
3858 return;
3860 if (pixels.IsNull())
3861 return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
3863 const ArrayBufferView& view = pixels.Value();
3864 view.ComputeLengthAndData();
3866 return TexSubImage2D_base(target, level, xoffset, yoffset,
3867 width, height, 0, format, type,
3868 view.Data(), view.Length(),
3869 JS_GetArrayBufferViewType(view.Obj()),
3870 WebGLTexelFormat::Auto, false);
3873 void
3874 WebGLContext::TexSubImage2D(GLenum target, GLint level,
3875 GLint xoffset, GLint yoffset,
3876 GLenum format, GLenum type, ImageData* pixels,
3877 ErrorResult& rv)
3879 if (IsContextLost())
3880 return;
3882 if (!pixels)
3883 return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
3885 Uint8ClampedArray arr;
3886 DebugOnly<bool> inited = arr.Init(pixels->GetDataObject());
3887 MOZ_ASSERT(inited);
3888 arr.ComputeLengthAndData();
3890 return TexSubImage2D_base(target, level, xoffset, yoffset,
3891 pixels->Width(), pixels->Height(),
3892 4*pixels->Width(), format, type,
3893 arr.Data(), arr.Length(),
3895 WebGLTexelFormat::RGBA8, false);
3898 void
3899 WebGLContext::LoseContext()
3901 if (IsContextLost())
3902 return ErrorInvalidOperation("loseContext: Context is already lost.");
3904 ForceLoseContext(true);
3907 void
3908 WebGLContext::RestoreContext()
3910 if (!IsContextLost())
3911 return ErrorInvalidOperation("restoreContext: Context is not lost.");
3913 if (!mLastLossWasSimulated) {
3914 return ErrorInvalidOperation("restoreContext: Context loss was not simulated."
3915 " Cannot simulate restore.");
3917 // If we're currently lost, and the last loss was simulated, then
3918 // we're currently only simulated-lost, allowing us to call
3919 // restoreContext().
3921 if (!mAllowContextRestore)
3922 return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
3924 ForceRestoreContext();
3927 bool
3928 BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize)
3930 switch (uType) {
3931 case LOCAL_GL_INT:
3932 case LOCAL_GL_INT_VEC2:
3933 case LOCAL_GL_INT_VEC3:
3934 case LOCAL_GL_INT_VEC4:
3935 case LOCAL_GL_SAMPLER_2D:
3936 case LOCAL_GL_SAMPLER_CUBE:
3937 *baseType = LOCAL_GL_INT;
3938 break;
3939 case LOCAL_GL_FLOAT:
3940 case LOCAL_GL_FLOAT_VEC2:
3941 case LOCAL_GL_FLOAT_VEC3:
3942 case LOCAL_GL_FLOAT_VEC4:
3943 case LOCAL_GL_FLOAT_MAT2:
3944 case LOCAL_GL_FLOAT_MAT3:
3945 case LOCAL_GL_FLOAT_MAT4:
3946 *baseType = LOCAL_GL_FLOAT;
3947 break;
3948 case LOCAL_GL_BOOL:
3949 case LOCAL_GL_BOOL_VEC2:
3950 case LOCAL_GL_BOOL_VEC3:
3951 case LOCAL_GL_BOOL_VEC4:
3952 *baseType = LOCAL_GL_BOOL; // pretend these are int
3953 break;
3954 default:
3955 return false;
3958 switch (uType) {
3959 case LOCAL_GL_INT:
3960 case LOCAL_GL_FLOAT:
3961 case LOCAL_GL_BOOL:
3962 case LOCAL_GL_SAMPLER_2D:
3963 case LOCAL_GL_SAMPLER_CUBE:
3964 *unitSize = 1;
3965 break;
3966 case LOCAL_GL_INT_VEC2:
3967 case LOCAL_GL_FLOAT_VEC2:
3968 case LOCAL_GL_BOOL_VEC2:
3969 *unitSize = 2;
3970 break;
3971 case LOCAL_GL_INT_VEC3:
3972 case LOCAL_GL_FLOAT_VEC3:
3973 case LOCAL_GL_BOOL_VEC3:
3974 *unitSize = 3;
3975 break;
3976 case LOCAL_GL_INT_VEC4:
3977 case LOCAL_GL_FLOAT_VEC4:
3978 case LOCAL_GL_BOOL_VEC4:
3979 *unitSize = 4;
3980 break;
3981 case LOCAL_GL_FLOAT_MAT2:
3982 *unitSize = 4;
3983 break;
3984 case LOCAL_GL_FLOAT_MAT3:
3985 *unitSize = 9;
3986 break;
3987 case LOCAL_GL_FLOAT_MAT4:
3988 *unitSize = 16;
3989 break;
3990 default:
3991 return false;
3994 return true;
3998 WebGLTexelFormat mozilla::GetWebGLTexelFormat(GLenum internalformat, GLenum type)
4001 // WEBGL_depth_texture
4002 if (internalformat == LOCAL_GL_DEPTH_COMPONENT) {
4003 switch (type) {
4004 case LOCAL_GL_UNSIGNED_SHORT:
4005 return WebGLTexelFormat::D16;
4006 case LOCAL_GL_UNSIGNED_INT:
4007 return WebGLTexelFormat::D32;
4010 MOZ_CRASH("Invalid WebGL texture format/type?");
4013 if (internalformat == LOCAL_GL_DEPTH_STENCIL) {
4014 switch (type) {
4015 case LOCAL_GL_UNSIGNED_INT_24_8_EXT:
4016 return WebGLTexelFormat::D24S8;
4019 MOZ_CRASH("Invalid WebGL texture format/type?");
4022 if (internalformat == LOCAL_GL_DEPTH_COMPONENT16) {
4023 return WebGLTexelFormat::D16;
4026 if (internalformat == LOCAL_GL_DEPTH_COMPONENT32) {
4027 return WebGLTexelFormat::D32;
4030 if (internalformat == LOCAL_GL_DEPTH24_STENCIL8) {
4031 return WebGLTexelFormat::D24S8;
4034 if (type == LOCAL_GL_UNSIGNED_BYTE) {
4035 switch (internalformat) {
4036 case LOCAL_GL_RGBA:
4037 case LOCAL_GL_SRGB_ALPHA_EXT:
4038 return WebGLTexelFormat::RGBA8;
4039 case LOCAL_GL_RGB:
4040 case LOCAL_GL_SRGB_EXT:
4041 return WebGLTexelFormat::RGB8;
4042 case LOCAL_GL_ALPHA:
4043 return WebGLTexelFormat::A8;
4044 case LOCAL_GL_LUMINANCE:
4045 return WebGLTexelFormat::R8;
4046 case LOCAL_GL_LUMINANCE_ALPHA:
4047 return WebGLTexelFormat::RA8;
4050 MOZ_CRASH("Invalid WebGL texture format/type?");
4053 if (type == LOCAL_GL_FLOAT) {
4054 // OES_texture_float
4055 switch (internalformat) {
4056 case LOCAL_GL_RGBA:
4057 case LOCAL_GL_RGBA32F:
4058 return WebGLTexelFormat::RGBA32F;
4059 case LOCAL_GL_RGB:
4060 case LOCAL_GL_RGB32F:
4061 return WebGLTexelFormat::RGB32F;
4062 case LOCAL_GL_ALPHA:
4063 case LOCAL_GL_ALPHA32F_ARB:
4064 return WebGLTexelFormat::A32F;
4065 case LOCAL_GL_LUMINANCE:
4066 case LOCAL_GL_LUMINANCE32F_ARB:
4067 return WebGLTexelFormat::R32F;
4068 case LOCAL_GL_LUMINANCE_ALPHA:
4069 case LOCAL_GL_LUMINANCE_ALPHA32F_ARB:
4070 return WebGLTexelFormat::RA32F;
4073 MOZ_CRASH("Invalid WebGL texture format/type?");
4074 } else if (type == LOCAL_GL_HALF_FLOAT_OES) {
4075 // OES_texture_half_float
4076 switch (internalformat) {
4077 case LOCAL_GL_RGBA:
4078 case LOCAL_GL_RGBA16F:
4079 return WebGLTexelFormat::RGBA16F;
4080 case LOCAL_GL_RGB:
4081 case LOCAL_GL_RGB16F:
4082 return WebGLTexelFormat::RGB16F;
4083 case LOCAL_GL_ALPHA:
4084 case LOCAL_GL_ALPHA16F_ARB:
4085 return WebGLTexelFormat::A16F;
4086 case LOCAL_GL_LUMINANCE:
4087 case LOCAL_GL_LUMINANCE16F_ARB:
4088 return WebGLTexelFormat::R16F;
4089 case LOCAL_GL_LUMINANCE_ALPHA:
4090 case LOCAL_GL_LUMINANCE_ALPHA16F_ARB:
4091 return WebGLTexelFormat::RA16F;
4092 default:
4093 MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
4094 return WebGLTexelFormat::BadFormat;
4098 switch (type) {
4099 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
4100 return WebGLTexelFormat::RGBA4444;
4101 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
4102 return WebGLTexelFormat::RGBA5551;
4103 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
4104 return WebGLTexelFormat::RGB565;
4105 default:
4106 MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
4107 return WebGLTexelFormat::BadFormat;
4110 MOZ_CRASH("Invalid WebGL texture format/type?");
4113 void
4114 WebGLContext::BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) {
4115 if (IsContextLost())
4116 return;
4117 MakeContextCurrent();
4118 gl->fBlendColor(r, g, b, a);
4121 void
4122 WebGLContext::Flush() {
4123 if (IsContextLost())
4124 return;
4125 MakeContextCurrent();
4126 gl->fFlush();
4129 void
4130 WebGLContext::Finish() {
4131 if (IsContextLost())
4132 return;
4133 MakeContextCurrent();
4134 gl->fFinish();
4137 void
4138 WebGLContext::LineWidth(GLfloat width) {
4139 if (IsContextLost())
4140 return;
4141 MakeContextCurrent();
4142 gl->fLineWidth(width);
4145 void
4146 WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) {
4147 if (IsContextLost())
4148 return;
4149 MakeContextCurrent();
4150 gl->fPolygonOffset(factor, units);
4153 void
4154 WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) {
4155 if (IsContextLost())
4156 return;
4157 MakeContextCurrent();
4158 gl->fSampleCoverage(value, invert);