Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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"
23 #include "gfxContext.h"
24 #include "gfxPlatform.h"
25 #include "GLContext.h"
27 #include "nsContentUtils.h"
28 #include "nsError.h"
29 #include "nsLayoutUtils.h"
31 #include "CanvasUtils.h"
32 #include "gfxUtils.h"
34 #include "jsfriendapi.h"
36 #include "WebGLTexelConversions.h"
37 #include "WebGLValidateStrings.h"
38 #include <algorithm>
40 // needed to check if current OS is lower than 10.7
41 #if defined(MOZ_WIDGET_COCOA)
42 #include "nsCocoaFeatures.h"
43 #endif
45 #include "mozilla/dom/BindingUtils.h"
46 #include "mozilla/dom/ImageData.h"
47 #include "mozilla/dom/ToJSValue.h"
48 #include "mozilla/Endian.h"
50 using namespace mozilla;
51 using namespace mozilla::dom;
52 using namespace mozilla::gl;
53 using namespace mozilla::gfx;
55 static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize);
57 const WebGLRectangleObject*
58 WebGLContext::CurValidFBRectObject() const
59 {
60 const WebGLRectangleObject* rect = nullptr;
62 if (mBoundFramebuffer) {
63 // We don't really need to ask the driver.
64 // Use 'precheck' to just check that our internal state looks good.
65 GLenum precheckStatus = mBoundFramebuffer->PrecheckFramebufferStatus();
66 if (precheckStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
67 rect = &mBoundFramebuffer->RectangleObject();
68 } else {
69 rect = static_cast<const WebGLRectangleObject*>(this);
70 }
72 return rect;
73 }
75 //
76 // WebGL API
77 //
79 void
80 WebGLContext::ActiveTexture(GLenum texture)
81 {
82 if (IsContextLost())
83 return;
85 if (texture < LOCAL_GL_TEXTURE0 ||
86 texture >= LOCAL_GL_TEXTURE0 + uint32_t(mGLMaxTextureUnits))
87 {
88 return ErrorInvalidEnum(
89 "ActiveTexture: texture unit %d out of range. "
90 "Accepted values range from TEXTURE0 to TEXTURE0 + %d. "
91 "Notice that TEXTURE0 != 0.",
92 texture, mGLMaxTextureUnits);
93 }
95 MakeContextCurrent();
96 mActiveTexture = texture - LOCAL_GL_TEXTURE0;
97 gl->fActiveTexture(texture);
98 }
100 void
101 WebGLContext::AttachShader(WebGLProgram *program, WebGLShader *shader)
102 {
103 if (IsContextLost())
104 return;
106 if (!ValidateObject("attachShader: program", program) ||
107 !ValidateObject("attachShader: shader", shader))
108 return;
110 // Per GLSL ES 2.0, we can only have one of each type of shader
111 // attached. This renders the next test somewhat moot, but we'll
112 // leave it for when we support more than one shader of each type.
113 if (program->HasAttachedShaderOfType(shader->ShaderType()))
114 return ErrorInvalidOperation("attachShader: only one of each type of shader may be attached to a program");
116 if (!program->AttachShader(shader))
117 return ErrorInvalidOperation("attachShader: shader is already attached");
118 }
121 void
122 WebGLContext::BindAttribLocation(WebGLProgram *prog, GLuint location,
123 const nsAString& name)
124 {
125 if (IsContextLost())
126 return;
128 if (!ValidateObject("bindAttribLocation: program", prog))
129 return;
131 GLuint progname = prog->GLName();
133 if (!ValidateGLSLVariableName(name, "bindAttribLocation"))
134 return;
136 if (!ValidateAttribIndex(location, "bindAttribLocation"))
137 return;
139 NS_LossyConvertUTF16toASCII cname(name);
140 nsCString mappedName;
141 prog->MapIdentifier(cname, &mappedName);
143 MakeContextCurrent();
144 gl->fBindAttribLocation(progname, location, mappedName.get());
145 }
147 void
148 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer *wfb)
149 {
150 if (IsContextLost())
151 return;
153 if (target != LOCAL_GL_FRAMEBUFFER)
154 return ErrorInvalidEnum("bindFramebuffer: target must be GL_FRAMEBUFFER");
156 if (!ValidateObjectAllowDeletedOrNull("bindFramebuffer", wfb))
157 return;
159 // silently ignore a deleted frame buffer
160 if (wfb && wfb->IsDeleted())
161 return;
163 MakeContextCurrent();
165 if (!wfb) {
166 gl->fBindFramebuffer(target, 0);
167 } else {
168 GLuint framebuffername = wfb->GLName();
169 gl->fBindFramebuffer(target, framebuffername);
170 wfb->SetHasEverBeenBound(true);
171 }
173 mBoundFramebuffer = wfb;
174 }
176 void
177 WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer *wrb)
178 {
179 if (IsContextLost())
180 return;
182 if (target != LOCAL_GL_RENDERBUFFER)
183 return ErrorInvalidEnumInfo("bindRenderbuffer: target", target);
185 if (!ValidateObjectAllowDeletedOrNull("bindRenderbuffer", wrb))
186 return;
188 // silently ignore a deleted buffer
189 if (wrb && wrb->IsDeleted())
190 return;
192 if (wrb)
193 wrb->SetHasEverBeenBound(true);
195 MakeContextCurrent();
197 // Sometimes we emulate renderbuffers (depth-stencil emu), so there's not
198 // always a 1-1 mapping from `wrb` to GL name. Just have `wrb` handle it.
199 if (wrb) {
200 wrb->BindRenderbuffer();
201 } else {
202 gl->fBindRenderbuffer(target, 0);
203 }
205 mBoundRenderbuffer = wrb;
206 }
208 void
209 WebGLContext::BindTexture(GLenum target, WebGLTexture *newTex)
210 {
211 if (IsContextLost())
212 return;
214 if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex))
215 return;
217 // silently ignore a deleted texture
218 if (newTex && newTex->IsDeleted())
219 return;
221 WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
223 if (target == LOCAL_GL_TEXTURE_2D) {
224 currentTexPtr = &mBound2DTextures[mActiveTexture];
225 } else if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
226 currentTexPtr = &mBoundCubeMapTextures[mActiveTexture];
227 } else {
228 return ErrorInvalidEnumInfo("bindTexture: target", target);
229 }
231 WebGLTextureFakeBlackStatus currentTexFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
232 if (*currentTexPtr) {
233 currentTexFakeBlackStatus = (*currentTexPtr)->ResolvedFakeBlackStatus();
234 }
235 WebGLTextureFakeBlackStatus newTexFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
236 if (newTex) {
237 newTexFakeBlackStatus = newTex->ResolvedFakeBlackStatus();
238 }
240 *currentTexPtr = newTex;
242 if (currentTexFakeBlackStatus != newTexFakeBlackStatus) {
243 SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown);
244 }
246 MakeContextCurrent();
248 if (newTex)
249 newTex->Bind(target);
250 else
251 gl->fBindTexture(target, 0 /* == texturename */);
252 }
254 void WebGLContext::BlendEquation(GLenum mode)
255 {
256 if (IsContextLost())
257 return;
259 if (!ValidateBlendEquationEnum(mode, "blendEquation: mode"))
260 return;
262 MakeContextCurrent();
263 gl->fBlendEquation(mode);
264 }
266 void WebGLContext::BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
267 {
268 if (IsContextLost())
269 return;
271 if (!ValidateBlendEquationEnum(modeRGB, "blendEquationSeparate: modeRGB") ||
272 !ValidateBlendEquationEnum(modeAlpha, "blendEquationSeparate: modeAlpha"))
273 return;
275 MakeContextCurrent();
276 gl->fBlendEquationSeparate(modeRGB, modeAlpha);
277 }
279 void WebGLContext::BlendFunc(GLenum sfactor, GLenum dfactor)
280 {
281 if (IsContextLost())
282 return;
284 if (!ValidateBlendFuncSrcEnum(sfactor, "blendFunc: sfactor") ||
285 !ValidateBlendFuncDstEnum(dfactor, "blendFunc: dfactor"))
286 return;
288 if (!ValidateBlendFuncEnumsCompatibility(sfactor, dfactor, "blendFuncSeparate: srcRGB and dstRGB"))
289 return;
291 MakeContextCurrent();
292 gl->fBlendFunc(sfactor, dfactor);
293 }
295 void
296 WebGLContext::BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
297 GLenum srcAlpha, GLenum dstAlpha)
298 {
299 if (IsContextLost())
300 return;
302 if (!ValidateBlendFuncSrcEnum(srcRGB, "blendFuncSeparate: srcRGB") ||
303 !ValidateBlendFuncSrcEnum(srcAlpha, "blendFuncSeparate: srcAlpha") ||
304 !ValidateBlendFuncDstEnum(dstRGB, "blendFuncSeparate: dstRGB") ||
305 !ValidateBlendFuncDstEnum(dstAlpha, "blendFuncSeparate: dstAlpha"))
306 return;
308 // note that we only check compatibity for the RGB enums, no need to for the Alpha enums, see
309 // "Section 6.8 forgetting to mention alpha factors?" thread on the public_webgl mailing list
310 if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "blendFuncSeparate: srcRGB and dstRGB"))
311 return;
313 MakeContextCurrent();
314 gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
315 }
317 GLenum
318 WebGLContext::CheckFramebufferStatus(GLenum target)
319 {
320 if (IsContextLost())
321 return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
323 if (target != LOCAL_GL_FRAMEBUFFER) {
324 ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER");
325 return 0;
326 }
328 if (!mBoundFramebuffer)
329 return LOCAL_GL_FRAMEBUFFER_COMPLETE;
331 return mBoundFramebuffer->CheckFramebufferStatus();
332 }
334 void
335 WebGLContext::CopyTexSubImage2D_base(GLenum target,
336 GLint level,
337 GLenum internalformat,
338 GLint xoffset,
339 GLint yoffset,
340 GLint x,
341 GLint y,
342 GLsizei width,
343 GLsizei height,
344 bool sub)
345 {
346 const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
347 GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
348 GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
350 const char* info = sub ? "copyTexSubImage2D" : "copyTexImage2D";
351 WebGLTexImageFunc func = sub ? WebGLTexImageFunc::CopyTexSubImage : WebGLTexImageFunc::CopyTexImage;
353 // TODO: This changes with color_buffer_float. Reassess when the
354 // patch lands.
355 if (!ValidateTexImage(2, target, level, internalformat,
356 xoffset, yoffset, 0,
357 width, height, 0,
358 0, internalformat, LOCAL_GL_UNSIGNED_BYTE,
359 func))
360 {
361 return;
362 }
364 MakeContextCurrent();
366 WebGLTexture *tex = activeBoundTextureForTarget(target);
368 if (!tex)
369 return ErrorInvalidOperation("%s: no texture is bound to this target");
371 if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
372 if (sub)
373 gl->fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
374 else
375 gl->fCopyTexImage2D(target, level, internalformat, x, y, width, height, 0);
376 } else {
378 // the rect doesn't fit in the framebuffer
380 /*** first, we initialize the texture as black ***/
382 // first, compute the size of the buffer we should allocate to initialize the texture as black
384 if (!ValidateTexInputData(LOCAL_GL_UNSIGNED_BYTE, -1, func))
385 return;
387 uint32_t texelSize = GetBitsPerTexel(internalformat, LOCAL_GL_UNSIGNED_BYTE) / 8;
389 CheckedUint32 checked_neededByteLength =
390 GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment);
392 if (!checked_neededByteLength.isValid())
393 return ErrorInvalidOperation("%s: integer overflow computing the needed buffer size", info);
395 uint32_t bytesNeeded = checked_neededByteLength.value();
397 // now that the size is known, create the buffer
399 // We need some zero pages, because GL doesn't guarantee the
400 // contents of a texture allocated with nullptr data.
401 // Hopefully calloc will just mmap zero pages here.
402 void* tempZeroData = calloc(1, bytesNeeded);
403 if (!tempZeroData)
404 return ErrorOutOfMemory("%s: could not allocate %d bytes (for zero fill)", info, bytesNeeded);
406 // now initialize the texture as black
408 if (sub)
409 gl->fTexSubImage2D(target, level, 0, 0, width, height,
410 internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData);
411 else
412 gl->fTexImage2D(target, level, internalformat, width, height,
413 0, internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData);
414 free(tempZeroData);
416 // if we are completely outside of the framebuffer, we can exit now with our black texture
417 if ( x >= framebufferWidth
418 || x+width <= 0
419 || y >= framebufferHeight
420 || y+height <= 0)
421 {
422 // we are completely outside of range, can exit now with buffer filled with zeros
423 return DummyFramebufferOperation(info);
424 }
426 GLint actual_x = clamped(x, 0, framebufferWidth);
427 GLint actual_x_plus_width = clamped(x + width, 0, framebufferWidth);
428 GLsizei actual_width = actual_x_plus_width - actual_x;
429 GLint actual_xoffset = xoffset + actual_x - x;
431 GLint actual_y = clamped(y, 0, framebufferHeight);
432 GLint actual_y_plus_height = clamped(y + height, 0, framebufferHeight);
433 GLsizei actual_height = actual_y_plus_height - actual_y;
434 GLint actual_yoffset = yoffset + actual_y - y;
436 gl->fCopyTexSubImage2D(target, level, actual_xoffset, actual_yoffset, actual_x, actual_y, actual_width, actual_height);
437 }
438 }
440 void
441 WebGLContext::CopyTexImage2D(GLenum target,
442 GLint level,
443 GLenum internalformat,
444 GLint x,
445 GLint y,
446 GLsizei width,
447 GLsizei height,
448 GLint border)
449 {
450 if (IsContextLost())
451 return;
453 // copyTexImage2D only generates textures with type = UNSIGNED_BYTE
454 const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage;
455 const GLenum format = internalformat; // WebGL/ES Format
456 const GLenum type = LOCAL_GL_UNSIGNED_BYTE; // WebGL/ES Format
458 if (!ValidateTexImage(2, target, level, format,
459 0, 0, 0,
460 width, height, 0,
461 border, format, type,
462 func))
463 {
464 return;
465 }
467 if (mBoundFramebuffer) {
468 if (!mBoundFramebuffer->CheckAndInitializeAttachments())
469 return ErrorInvalidFramebufferOperation("copyTexImage2D: incomplete framebuffer");
471 GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
472 if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
473 return ErrorInvalidOperation("copyTexImage2D: Read source attachment doesn't have the"
474 " correct color/depth/stencil type.");
475 }
476 } else {
477 ClearBackbufferIfNeeded();
478 }
480 bool texFormatRequiresAlpha = format == LOCAL_GL_RGBA ||
481 format == LOCAL_GL_ALPHA ||
482 format == LOCAL_GL_LUMINANCE_ALPHA;
483 bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
484 : bool(gl->GetPixelFormat().alpha > 0);
485 if (texFormatRequiresAlpha && !fboFormatHasAlpha)
486 return ErrorInvalidOperation("copyTexImage2D: texture format requires an alpha channel "
487 "but the framebuffer doesn't have one");
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();
499 }
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;
511 }
512 }
514 tex->SetImageInfo(target, level, width, height, format, type,
515 WebGLImageDataStatus::InitializedImageData);
516 }
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)
527 {
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);
542 }
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 GLenum webGLFormat = imageInfo.WebGLFormat();
575 if (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat))
576 return ErrorInvalidOperation("copyTexSubImage2D: a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
578 if (mBoundFramebuffer) {
579 if (!mBoundFramebuffer->CheckAndInitializeAttachments())
580 return ErrorInvalidFramebufferOperation("copyTexSubImage2D: incomplete framebuffer");
582 GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
583 if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
584 return ErrorInvalidOperation("copyTexSubImage2D: Read source attachment doesn't have the"
585 " correct color/depth/stencil type.");
586 }
587 } else {
588 ClearBackbufferIfNeeded();
589 }
591 bool texFormatRequiresAlpha = FormatHasAlpha(webGLFormat);
592 bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
593 : bool(gl->GetPixelFormat().alpha > 0);
595 if (texFormatRequiresAlpha && !fboFormatHasAlpha)
596 return ErrorInvalidOperation("copyTexSubImage2D: texture format requires an alpha channel "
597 "but the framebuffer doesn't have one");
599 if (imageInfo.HasUninitializedImageData()) {
600 tex->DoDeferredImageInitialization(target, level);
601 }
603 return CopyTexSubImage2D_base(target, level, webGLFormat, xoffset, yoffset, x, y, width, height, true);
604 }
607 already_AddRefed<WebGLProgram>
608 WebGLContext::CreateProgram()
609 {
610 if (IsContextLost())
611 return nullptr;
612 nsRefPtr<WebGLProgram> globj = new WebGLProgram(this);
613 return globj.forget();
614 }
616 already_AddRefed<WebGLShader>
617 WebGLContext::CreateShader(GLenum type)
618 {
619 if (IsContextLost())
620 return nullptr;
622 if (type != LOCAL_GL_VERTEX_SHADER &&
623 type != LOCAL_GL_FRAGMENT_SHADER)
624 {
625 ErrorInvalidEnumInfo("createShader: type", type);
626 return nullptr;
627 }
629 nsRefPtr<WebGLShader> shader = new WebGLShader(this, type);
630 return shader.forget();
631 }
633 void
634 WebGLContext::CullFace(GLenum face)
635 {
636 if (IsContextLost())
637 return;
639 if (!ValidateFaceEnum(face, "cullFace"))
640 return;
642 MakeContextCurrent();
643 gl->fCullFace(face);
644 }
646 void
647 WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf)
648 {
649 if (IsContextLost())
650 return;
652 if (!ValidateObjectAllowDeletedOrNull("deleteFramebuffer", fbuf))
653 return;
655 if (!fbuf || fbuf->IsDeleted())
656 return;
658 fbuf->RequestDelete();
660 if (mBoundFramebuffer == fbuf)
661 BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
662 static_cast<WebGLFramebuffer*>(nullptr));
663 }
665 void
666 WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer *rbuf)
667 {
668 if (IsContextLost())
669 return;
671 if (!ValidateObjectAllowDeletedOrNull("deleteRenderbuffer", rbuf))
672 return;
674 if (!rbuf || rbuf->IsDeleted())
675 return;
677 if (mBoundFramebuffer)
678 mBoundFramebuffer->DetachRenderbuffer(rbuf);
680 // Invalidate framebuffer status cache
681 rbuf->NotifyFBsStatusChanged();
683 if (mBoundRenderbuffer == rbuf)
684 BindRenderbuffer(LOCAL_GL_RENDERBUFFER,
685 static_cast<WebGLRenderbuffer*>(nullptr));
687 rbuf->RequestDelete();
688 }
690 void
691 WebGLContext::DeleteTexture(WebGLTexture *tex)
692 {
693 if (IsContextLost())
694 return;
696 if (!ValidateObjectAllowDeletedOrNull("deleteTexture", tex))
697 return;
699 if (!tex || tex->IsDeleted())
700 return;
702 if (mBoundFramebuffer)
703 mBoundFramebuffer->DetachTexture(tex);
705 // Invalidate framebuffer status cache
706 tex->NotifyFBsStatusChanged();
708 GLuint activeTexture = mActiveTexture;
709 for (int32_t i = 0; i < mGLMaxTextureUnits; i++) {
710 if ((tex->Target() == LOCAL_GL_TEXTURE_2D && mBound2DTextures[i] == tex) ||
711 (tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP && mBoundCubeMapTextures[i] == tex))
712 {
713 ActiveTexture(LOCAL_GL_TEXTURE0 + i);
714 BindTexture(tex->Target(), static_cast<WebGLTexture*>(nullptr));
715 }
716 }
717 ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
719 tex->RequestDelete();
720 }
722 void
723 WebGLContext::DeleteProgram(WebGLProgram *prog)
724 {
725 if (IsContextLost())
726 return;
728 if (!ValidateObjectAllowDeletedOrNull("deleteProgram", prog))
729 return;
731 if (!prog || prog->IsDeleted())
732 return;
734 prog->RequestDelete();
735 }
737 void
738 WebGLContext::DeleteShader(WebGLShader *shader)
739 {
740 if (IsContextLost())
741 return;
743 if (!ValidateObjectAllowDeletedOrNull("deleteShader", shader))
744 return;
746 if (!shader || shader->IsDeleted())
747 return;
749 shader->RequestDelete();
750 }
752 void
753 WebGLContext::DetachShader(WebGLProgram *program, WebGLShader *shader)
754 {
755 if (IsContextLost())
756 return;
758 if (!ValidateObject("detachShader: program", program) ||
759 // it's valid to attempt to detach a deleted shader, since it's
760 // still a shader
761 !ValidateObjectAllowDeleted("detashShader: shader", shader))
762 return;
764 if (!program->DetachShader(shader))
765 return ErrorInvalidOperation("detachShader: shader is not attached");
766 }
768 void
769 WebGLContext::DepthFunc(GLenum func)
770 {
771 if (IsContextLost())
772 return;
774 if (!ValidateComparisonEnum(func, "depthFunc"))
775 return;
777 MakeContextCurrent();
778 gl->fDepthFunc(func);
779 }
781 void
782 WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar)
783 {
784 if (IsContextLost())
785 return;
787 if (zNear > zFar)
788 return ErrorInvalidOperation("depthRange: the near value is greater than the far value!");
790 MakeContextCurrent();
791 gl->fDepthRange(zNear, zFar);
792 }
794 void
795 WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum rbtarget, WebGLRenderbuffer *wrb)
796 {
797 if (IsContextLost())
798 return;
800 if (!mBoundFramebuffer)
801 return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0");
803 return mBoundFramebuffer->FramebufferRenderbuffer(target, attachment, rbtarget, wrb);
804 }
806 void
807 WebGLContext::FramebufferTexture2D(GLenum target,
808 GLenum attachment,
809 GLenum textarget,
810 WebGLTexture *tobj,
811 GLint level)
812 {
813 if (IsContextLost())
814 return;
816 if (!mBoundFramebuffer)
817 return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0");
819 return mBoundFramebuffer->FramebufferTexture2D(target, attachment, textarget, tobj, level);
820 }
822 void
823 WebGLContext::FrontFace(GLenum mode)
824 {
825 if (IsContextLost())
826 return;
828 switch (mode) {
829 case LOCAL_GL_CW:
830 case LOCAL_GL_CCW:
831 break;
832 default:
833 return ErrorInvalidEnumInfo("frontFace: mode", mode);
834 }
836 MakeContextCurrent();
837 gl->fFrontFace(mode);
838 }
840 already_AddRefed<WebGLActiveInfo>
841 WebGLContext::GetActiveAttrib(WebGLProgram *prog, uint32_t index)
842 {
843 if (IsContextLost())
844 return nullptr;
846 if (!ValidateObject("getActiveAttrib: program", prog))
847 return nullptr;
849 MakeContextCurrent();
851 GLint len = 0;
852 GLuint progname = prog->GLName();;
853 gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len);
854 if (len == 0)
855 return nullptr;
857 nsAutoArrayPtr<char> name(new char[len]);
858 GLint attrsize = 0;
859 GLuint attrtype = 0;
861 gl->fGetActiveAttrib(progname, index, len, &len, &attrsize, &attrtype, name);
862 if (attrsize == 0 || attrtype == 0) {
863 return nullptr;
864 }
866 nsCString reverseMappedName;
867 prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName);
869 nsRefPtr<WebGLActiveInfo> retActiveInfo =
870 new WebGLActiveInfo(attrsize, attrtype, reverseMappedName);
871 return retActiveInfo.forget();
872 }
874 void
875 WebGLContext::GenerateMipmap(GLenum target)
876 {
877 if (IsContextLost())
878 return;
880 if (!ValidateTextureTargetEnum(target, "generateMipmap"))
881 return;
883 WebGLTexture *tex = activeBoundTextureForTarget(target);
885 if (!tex)
886 return ErrorInvalidOperation("generateMipmap: No texture is bound to this target.");
888 GLenum imageTarget = (target == LOCAL_GL_TEXTURE_2D) ? LOCAL_GL_TEXTURE_2D
889 : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
890 if (!tex->HasImageInfoAt(imageTarget, 0))
891 {
892 return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
893 }
895 if (!tex->IsFirstImagePowerOfTwo())
896 return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
898 GLenum webGLFormat = tex->ImageInfoAt(imageTarget, 0).WebGLFormat();
899 if (IsTextureFormatCompressed(webGLFormat))
900 return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");
902 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
903 (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat)))
904 {
905 return ErrorInvalidOperation("generateMipmap: "
906 "A texture that has a base internal format of "
907 "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
908 }
910 if (!tex->AreAllLevel0ImageInfosEqual())
911 return ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type.");
913 tex->SetGeneratedMipmap();
915 MakeContextCurrent();
917 if (gl->WorkAroundDriverBugs()) {
918 // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we
919 // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance
920 // overhead so we do it unconditionally.
921 //
922 // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105.
923 gl->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST_MIPMAP_NEAREST);
924 gl->fGenerateMipmap(target);
925 gl->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, tex->MinFilter());
926 } else {
927 gl->fGenerateMipmap(target);
928 }
929 }
931 already_AddRefed<WebGLActiveInfo>
932 WebGLContext::GetActiveUniform(WebGLProgram *prog, uint32_t index)
933 {
934 if (IsContextLost())
935 return nullptr;
937 if (!ValidateObject("getActiveUniform: program", prog))
938 return nullptr;
940 MakeContextCurrent();
942 GLint len = 0;
943 GLuint progname = prog->GLName();
944 gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &len);
945 if (len == 0)
946 return nullptr;
948 nsAutoArrayPtr<char> name(new char[len]);
950 GLint usize = 0;
951 GLuint utype = 0;
953 gl->fGetActiveUniform(progname, index, len, &len, &usize, &utype, name);
954 if (len == 0 || usize == 0 || utype == 0) {
955 return nullptr;
956 }
958 nsCString reverseMappedName;
959 prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName);
961 // OpenGL ES 2.0 specifies that if foo is a uniform array, GetActiveUniform returns its name as "foo[0]".
962 // See section 2.10 page 35 in the OpenGL ES 2.0.24 specification:
963 //
964 // > If the active uniform is an array, the uniform name returned in name will always
965 // > be the name of the uniform array appended with "[0]".
966 //
967 // There is no such requirement in the OpenGL (non-ES) spec and indeed we have OpenGL implementations returning
968 // "foo" instead of "foo[0]". So, when implementing WebGL on top of desktop OpenGL, we must check if the
969 // returned name ends in [0], and if it doesn't, append that.
970 //
971 // 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
972 // specs that it seems probable that some ES implementers will overlook it. Since the work-around is quite cheap,
973 // we do it unconditionally.
974 if (usize > 1 && reverseMappedName.CharAt(reverseMappedName.Length()-1) != ']')
975 reverseMappedName.AppendLiteral("[0]");
977 nsRefPtr<WebGLActiveInfo> retActiveInfo =
978 new WebGLActiveInfo(usize, utype, reverseMappedName);
979 return retActiveInfo.forget();
980 }
982 void
983 WebGLContext::GetAttachedShaders(WebGLProgram *prog,
984 Nullable< nsTArray<WebGLShader*> > &retval)
985 {
986 retval.SetNull();
987 if (IsContextLost())
988 return;
990 if (!ValidateObjectAllowNull("getAttachedShaders", prog))
991 return;
993 MakeContextCurrent();
995 if (!prog) {
996 retval.SetNull();
997 ErrorInvalidValue("getAttachedShaders: invalid program");
998 } else if (prog->AttachedShaders().Length() == 0) {
999 retval.SetValue().TruncateLength(0);
1000 } else {
1001 retval.SetValue().AppendElements(prog->AttachedShaders());
1002 }
1003 }
1005 GLint
1006 WebGLContext::GetAttribLocation(WebGLProgram *prog, const nsAString& name)
1007 {
1008 if (IsContextLost())
1009 return -1;
1011 if (!ValidateObject("getAttribLocation: program", prog))
1012 return -1;
1014 if (!ValidateGLSLVariableName(name, "getAttribLocation"))
1015 return -1;
1017 NS_LossyConvertUTF16toASCII cname(name);
1018 nsCString mappedName;
1019 prog->MapIdentifier(cname, &mappedName);
1021 GLuint progname = prog->GLName();
1023 MakeContextCurrent();
1024 return gl->fGetAttribLocation(progname, mappedName.get());
1025 }
1027 JS::Value
1028 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
1029 {
1030 if (IsContextLost())
1031 return JS::NullValue();
1033 if (target != LOCAL_GL_ARRAY_BUFFER && target != LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
1034 ErrorInvalidEnumInfo("getBufferParameter: target", target);
1035 return JS::NullValue();
1036 }
1038 MakeContextCurrent();
1040 switch (pname) {
1041 case LOCAL_GL_BUFFER_SIZE:
1042 case LOCAL_GL_BUFFER_USAGE:
1043 {
1044 GLint i = 0;
1045 gl->fGetBufferParameteriv(target, pname, &i);
1046 if (pname == LOCAL_GL_BUFFER_SIZE) {
1047 return JS::Int32Value(i);
1048 }
1050 MOZ_ASSERT(pname == LOCAL_GL_BUFFER_USAGE);
1051 return JS::NumberValue(uint32_t(i));
1052 }
1053 break;
1055 default:
1056 ErrorInvalidEnumInfo("getBufferParameter: parameter", pname);
1057 }
1059 return JS::NullValue();
1060 }
1062 JS::Value
1063 WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx,
1064 GLenum target,
1065 GLenum attachment,
1066 GLenum pname,
1067 ErrorResult& rv)
1068 {
1069 if (IsContextLost())
1070 return JS::NullValue();
1072 if (target != LOCAL_GL_FRAMEBUFFER) {
1073 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: target", target);
1074 return JS::NullValue();
1075 }
1077 if (!mBoundFramebuffer) {
1078 ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query framebuffer 0");
1079 return JS::NullValue();
1080 }
1082 if (attachment != LOCAL_GL_DEPTH_ATTACHMENT &&
1083 attachment != LOCAL_GL_STENCIL_ATTACHMENT &&
1084 attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
1085 {
1086 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
1087 {
1088 if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
1089 attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mGLMaxColorAttachments))
1090 {
1091 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: attachment", attachment);
1092 return JS::NullValue();
1093 }
1095 mBoundFramebuffer->EnsureColorAttachments(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
1096 }
1097 else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
1098 {
1099 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: attachment", attachment);
1100 return JS::NullValue();
1101 }
1102 }
1104 MakeContextCurrent();
1106 const WebGLFramebuffer::Attachment& fba = mBoundFramebuffer->GetAttachment(attachment);
1108 if (fba.Renderbuffer()) {
1109 switch (pname) {
1110 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
1111 if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
1112 const GLenum internalFormat = fba.Renderbuffer()->InternalFormat();
1113 return (internalFormat == LOCAL_GL_SRGB_EXT ||
1114 internalFormat == LOCAL_GL_SRGB_ALPHA_EXT ||
1115 internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT) ?
1116 JS::NumberValue(uint32_t(LOCAL_GL_SRGB_EXT)) :
1117 JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
1118 }
1119 break;
1121 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
1122 return JS::NumberValue(uint32_t(LOCAL_GL_RENDERBUFFER));
1124 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
1125 return WebGLObjectAsJSValue(cx, fba.Renderbuffer(), rv);
1127 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: {
1128 if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) &&
1129 !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
1130 {
1131 break;
1132 }
1134 if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1135 ErrorInvalidOperation("getFramebufferAttachmentParameter: Cannot get component"
1136 " type of a depth-stencil attachment.");
1137 return JS::NullValue();
1138 }
1140 if (!fba.IsComplete())
1141 return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
1143 uint32_t ret = LOCAL_GL_NONE;
1144 switch (fba.Renderbuffer()->InternalFormat()) {
1145 case LOCAL_GL_RGBA4:
1146 case LOCAL_GL_RGB5_A1:
1147 case LOCAL_GL_RGB565:
1148 case LOCAL_GL_SRGB8_ALPHA8:
1149 ret = LOCAL_GL_UNSIGNED_NORMALIZED;
1150 break;
1151 case LOCAL_GL_RGB16F:
1152 case LOCAL_GL_RGBA16F:
1153 case LOCAL_GL_RGB32F:
1154 case LOCAL_GL_RGBA32F:
1155 ret = LOCAL_GL_FLOAT;
1156 break;
1157 case LOCAL_GL_DEPTH_COMPONENT16:
1158 case LOCAL_GL_STENCIL_INDEX8:
1159 ret = LOCAL_GL_UNSIGNED_INT;
1160 break;
1161 default:
1162 MOZ_ASSERT(false, "Unhandled RB component type.");
1163 break;
1164 }
1165 return JS::NumberValue(uint32_t(ret));
1166 }
1167 }
1169 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
1170 return JS::NullValue();
1171 } else if (fba.Texture()) {
1172 switch (pname) {
1173 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
1174 if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
1175 const GLenum webGLFormat =
1176 fba.Texture()->ImageInfoBase().WebGLFormat();
1177 return (webGLFormat == LOCAL_GL_SRGB ||
1178 webGLFormat == LOCAL_GL_SRGB_ALPHA) ?
1179 JS::NumberValue(uint32_t(LOCAL_GL_SRGB)) :
1180 JS::NumberValue(uint32_t(LOCAL_GL_LINEAR));
1181 }
1182 break;
1184 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
1185 return JS::NumberValue(uint32_t(LOCAL_GL_TEXTURE));
1187 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
1188 return WebGLObjectAsJSValue(cx, fba.Texture(), rv);
1190 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
1191 return JS::Int32Value(fba.TexImageLevel());
1193 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: {
1194 GLenum face = fba.TexImageTarget();
1195 if (face == LOCAL_GL_TEXTURE_2D)
1196 face = 0;
1197 return JS::Int32Value(face);
1198 }
1200 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: {
1201 if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) &&
1202 !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
1203 {
1204 break;
1205 }
1207 if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
1208 ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot component"
1209 " type of depth-stencil attachments.");
1210 return JS::NullValue();
1211 }
1213 if (!fba.IsComplete())
1214 return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
1216 uint32_t ret = LOCAL_GL_NONE;
1217 GLenum type = fba.Texture()->ImageInfoAt(fba.TexImageTarget(),
1218 fba.TexImageLevel()).WebGLType();
1219 switch (type) {
1220 case LOCAL_GL_UNSIGNED_BYTE:
1221 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
1222 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
1223 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
1224 ret = LOCAL_GL_UNSIGNED_NORMALIZED;
1225 break;
1226 case LOCAL_GL_FLOAT:
1227 case LOCAL_GL_HALF_FLOAT_OES:
1228 ret = LOCAL_GL_FLOAT;
1229 break;
1230 case LOCAL_GL_UNSIGNED_SHORT:
1231 case LOCAL_GL_UNSIGNED_INT:
1232 ret = LOCAL_GL_UNSIGNED_INT;
1233 break;
1234 default:
1235 MOZ_ASSERT(false, "Unhandled RB component type.");
1236 break;
1237 }
1238 return JS::NumberValue(uint32_t(ret));
1239 }
1240 }
1242 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
1243 return JS::NullValue();
1244 } else {
1245 switch (pname) {
1246 case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
1247 return JS::NumberValue(uint32_t(LOCAL_GL_NONE));
1249 default:
1250 ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname);
1251 return JS::NullValue();
1252 }
1253 }
1255 return JS::NullValue();
1256 }
1258 JS::Value
1259 WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname)
1260 {
1261 if (IsContextLost())
1262 return JS::NullValue();
1264 if (target != LOCAL_GL_RENDERBUFFER) {
1265 ErrorInvalidEnumInfo("getRenderbufferParameter: target", target);
1266 return JS::NullValue();
1267 }
1269 if (!mBoundRenderbuffer) {
1270 ErrorInvalidOperation("getRenderbufferParameter: no render buffer is bound");
1271 return JS::NullValue();
1272 }
1274 MakeContextCurrent();
1276 switch (pname) {
1277 case LOCAL_GL_RENDERBUFFER_WIDTH:
1278 case LOCAL_GL_RENDERBUFFER_HEIGHT:
1279 case LOCAL_GL_RENDERBUFFER_RED_SIZE:
1280 case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
1281 case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
1282 case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
1283 case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
1284 case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
1285 {
1286 // RB emulation means we have to ask the RB itself.
1287 GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname);
1288 return JS::Int32Value(i);
1289 }
1290 case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
1291 {
1292 return JS::NumberValue(mBoundRenderbuffer->InternalFormat());
1293 }
1294 default:
1295 ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname);
1296 }
1298 return JS::NullValue();
1299 }
1301 already_AddRefed<WebGLTexture>
1302 WebGLContext::CreateTexture()
1303 {
1304 if (IsContextLost())
1305 return nullptr;
1306 nsRefPtr<WebGLTexture> globj = new WebGLTexture(this);
1307 return globj.forget();
1308 }
1310 static GLenum
1311 GetAndClearError(GLenum* errorVar)
1312 {
1313 MOZ_ASSERT(errorVar);
1314 GLenum ret = *errorVar;
1315 *errorVar = LOCAL_GL_NO_ERROR;
1316 return ret;
1317 }
1319 GLenum
1320 WebGLContext::GetError()
1321 {
1322 /* WebGL 1.0: Section 5.14.3: Setting and getting state:
1323 * If the context's webgl context lost flag is set, returns
1324 * CONTEXT_LOST_WEBGL the first time this method is called.
1325 * Afterward, returns NO_ERROR until the context has been
1326 * restored.
1327 *
1328 * WEBGL_lose_context:
1329 * [When this extension is enabled: ] loseContext and
1330 * restoreContext are allowed to generate INVALID_OPERATION errors
1331 * even when the context is lost.
1332 */
1334 if (IsContextLost()) {
1335 if (mEmitContextLostErrorOnce) {
1336 mEmitContextLostErrorOnce = false;
1337 return LOCAL_GL_CONTEXT_LOST;
1338 }
1339 // Don't return yet, since WEBGL_lose_contexts contradicts the
1340 // original spec, and allows error generation while lost.
1341 }
1343 GLenum err = GetAndClearError(&mWebGLError);
1344 if (err != LOCAL_GL_NO_ERROR)
1345 return err;
1347 if (IsContextLost())
1348 return LOCAL_GL_NO_ERROR;
1350 // Either no WebGL-side error, or it's already been cleared.
1351 // UnderlyingGL-side errors, now.
1353 MakeContextCurrent();
1354 GetAndFlushUnderlyingGLErrors();
1356 err = GetAndClearError(&mUnderlyingGLError);
1357 return err;
1358 }
1360 JS::Value
1361 WebGLContext::GetProgramParameter(WebGLProgram *prog, GLenum pname)
1362 {
1363 if (IsContextLost())
1364 return JS::NullValue();
1366 if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
1367 return JS::NullValue();
1369 GLuint progname = prog->GLName();
1371 MakeContextCurrent();
1373 switch (pname) {
1374 case LOCAL_GL_ATTACHED_SHADERS:
1375 case LOCAL_GL_ACTIVE_UNIFORMS:
1376 case LOCAL_GL_ACTIVE_ATTRIBUTES:
1377 {
1378 GLint i = 0;
1379 gl->fGetProgramiv(progname, pname, &i);
1380 return JS::Int32Value(i);
1381 }
1382 case LOCAL_GL_DELETE_STATUS:
1383 return JS::BooleanValue(prog->IsDeleteRequested());
1384 case LOCAL_GL_LINK_STATUS:
1385 {
1386 return JS::BooleanValue(prog->LinkStatus());
1387 }
1388 case LOCAL_GL_VALIDATE_STATUS:
1389 {
1390 GLint i = 0;
1391 #ifdef XP_MACOSX
1392 // See comment in ValidateProgram below.
1393 if (gl->WorkAroundDriverBugs())
1394 i = 1;
1395 else
1396 gl->fGetProgramiv(progname, pname, &i);
1397 #else
1398 gl->fGetProgramiv(progname, pname, &i);
1399 #endif
1400 return JS::BooleanValue(bool(i));
1401 }
1402 break;
1404 default:
1405 ErrorInvalidEnumInfo("getProgramParameter: parameter", pname);
1406 }
1408 return JS::NullValue();
1409 }
1411 void
1412 WebGLContext::GetProgramInfoLog(WebGLProgram *prog, nsAString& retval)
1413 {
1414 nsAutoCString s;
1415 GetProgramInfoLog(prog, s);
1416 if (s.IsVoid())
1417 retval.SetIsVoid(true);
1418 else
1419 CopyASCIItoUTF16(s, retval);
1420 }
1422 void
1423 WebGLContext::GetProgramInfoLog(WebGLProgram *prog, nsACString& retval)
1424 {
1425 if (IsContextLost())
1426 {
1427 retval.SetIsVoid(true);
1428 return;
1429 }
1431 if (!ValidateObject("getProgramInfoLog: program", prog)) {
1432 retval.Truncate();
1433 return;
1434 }
1436 GLuint progname = prog->GLName();
1438 MakeContextCurrent();
1440 GLint k = -1;
1441 gl->fGetProgramiv(progname, LOCAL_GL_INFO_LOG_LENGTH, &k);
1442 if (k == -1) {
1443 // If GetProgramiv doesn't modify |k|,
1444 // it's because there was a GL error.
1445 // GetProgramInfoLog should return null on error. (Bug 746740)
1446 retval.SetIsVoid(true);
1447 return;
1448 }
1450 if (k == 0) {
1451 retval.Truncate();
1452 return;
1453 }
1455 retval.SetCapacity(k);
1456 gl->fGetProgramInfoLog(progname, k, &k, (char*) retval.BeginWriting());
1457 retval.SetLength(k);
1458 }
1460 // here we have to support all pnames with both int and float params.
1461 // See this discussion:
1462 // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
1463 void WebGLContext::TexParameter_base(GLenum target, GLenum pname,
1464 GLint *intParamPtr,
1465 GLfloat *floatParamPtr)
1466 {
1467 MOZ_ASSERT(intParamPtr || floatParamPtr);
1469 if (IsContextLost())
1470 return;
1472 GLint intParam = intParamPtr ? *intParamPtr : GLint(*floatParamPtr);
1473 GLfloat floatParam = floatParamPtr ? *floatParamPtr : GLfloat(*intParamPtr);
1475 if (!ValidateTextureTargetEnum(target, "texParameter: target"))
1476 return;
1478 WebGLTexture *tex = activeBoundTextureForTarget(target);
1479 if (!tex)
1480 return ErrorInvalidOperation("texParameter: no texture is bound to this target");
1482 bool pnameAndParamAreIncompatible = false;
1483 bool paramValueInvalid = false;
1485 switch (pname) {
1486 case LOCAL_GL_TEXTURE_MIN_FILTER:
1487 switch (intParam) {
1488 case LOCAL_GL_NEAREST:
1489 case LOCAL_GL_LINEAR:
1490 case LOCAL_GL_NEAREST_MIPMAP_NEAREST:
1491 case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
1492 case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
1493 case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
1494 tex->SetMinFilter(intParam);
1495 break;
1496 default:
1497 pnameAndParamAreIncompatible = true;
1498 }
1499 break;
1500 case LOCAL_GL_TEXTURE_MAG_FILTER:
1501 switch (intParam) {
1502 case LOCAL_GL_NEAREST:
1503 case LOCAL_GL_LINEAR:
1504 tex->SetMagFilter(intParam);
1505 break;
1506 default:
1507 pnameAndParamAreIncompatible = true;
1508 }
1509 break;
1510 case LOCAL_GL_TEXTURE_WRAP_S:
1511 switch (intParam) {
1512 case LOCAL_GL_CLAMP_TO_EDGE:
1513 case LOCAL_GL_MIRRORED_REPEAT:
1514 case LOCAL_GL_REPEAT:
1515 tex->SetWrapS(intParam);
1516 break;
1517 default:
1518 pnameAndParamAreIncompatible = true;
1519 }
1520 break;
1521 case LOCAL_GL_TEXTURE_WRAP_T:
1522 switch (intParam) {
1523 case LOCAL_GL_CLAMP_TO_EDGE:
1524 case LOCAL_GL_MIRRORED_REPEAT:
1525 case LOCAL_GL_REPEAT:
1526 tex->SetWrapT(intParam);
1527 break;
1528 default:
1529 pnameAndParamAreIncompatible = true;
1530 }
1531 break;
1532 case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1533 if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
1534 if (floatParamPtr && floatParam < 1.f)
1535 paramValueInvalid = true;
1536 else if (intParamPtr && intParam < 1)
1537 paramValueInvalid = true;
1538 }
1539 else
1540 pnameAndParamAreIncompatible = true;
1541 break;
1542 default:
1543 return ErrorInvalidEnumInfo("texParameter: pname", pname);
1544 }
1546 if (pnameAndParamAreIncompatible) {
1547 if (intParamPtr)
1548 return ErrorInvalidEnum("texParameteri: pname %x and param %x (decimal %d) are mutually incompatible",
1549 pname, intParam, intParam);
1550 else
1551 return ErrorInvalidEnum("texParameterf: pname %x and param %g are mutually incompatible",
1552 pname, floatParam);
1553 } else if (paramValueInvalid) {
1554 if (intParamPtr)
1555 return ErrorInvalidValue("texParameteri: pname %x and param %x (decimal %d) is invalid",
1556 pname, intParam, intParam);
1557 else
1558 return ErrorInvalidValue("texParameterf: pname %x and param %g is invalid",
1559 pname, floatParam);
1560 }
1562 MakeContextCurrent();
1563 if (intParamPtr)
1564 gl->fTexParameteri(target, pname, intParam);
1565 else
1566 gl->fTexParameterf(target, pname, floatParam);
1567 }
1569 JS::Value
1570 WebGLContext::GetTexParameter(GLenum target, GLenum pname)
1571 {
1572 if (IsContextLost())
1573 return JS::NullValue();
1575 MakeContextCurrent();
1577 if (!ValidateTextureTargetEnum(target, "getTexParameter: target"))
1578 return JS::NullValue();
1580 if (!activeBoundTextureForTarget(target)) {
1581 ErrorInvalidOperation("getTexParameter: no texture bound");
1582 return JS::NullValue();
1583 }
1585 switch (pname) {
1586 case LOCAL_GL_TEXTURE_MIN_FILTER:
1587 case LOCAL_GL_TEXTURE_MAG_FILTER:
1588 case LOCAL_GL_TEXTURE_WRAP_S:
1589 case LOCAL_GL_TEXTURE_WRAP_T:
1590 {
1591 GLint i = 0;
1592 gl->fGetTexParameteriv(target, pname, &i);
1593 return JS::NumberValue(uint32_t(i));
1594 }
1595 case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
1596 if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
1597 GLfloat f = 0.f;
1598 gl->fGetTexParameterfv(target, pname, &f);
1599 return JS::DoubleValue(f);
1600 }
1602 ErrorInvalidEnumInfo("getTexParameter: parameter", pname);
1603 break;
1605 default:
1606 ErrorInvalidEnumInfo("getTexParameter: parameter", pname);
1607 }
1609 return JS::NullValue();
1610 }
1612 JS::Value
1613 WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog,
1614 WebGLUniformLocation *location)
1615 {
1616 if (IsContextLost())
1617 return JS::NullValue();
1619 if (!ValidateObject("getUniform: program", prog))
1620 return JS::NullValue();
1622 if (!ValidateObject("getUniform: location", location))
1623 return JS::NullValue();
1625 if (location->Program() != prog) {
1626 ErrorInvalidValue("getUniform: this uniform location corresponds to another program");
1627 return JS::NullValue();
1628 }
1630 if (location->ProgramGeneration() != prog->Generation()) {
1631 ErrorInvalidOperation("getUniform: this uniform location is obsolete since the program has been relinked");
1632 return JS::NullValue();
1633 }
1635 GLuint progname = prog->GLName();
1637 MakeContextCurrent();
1639 GLint uniforms = 0;
1640 GLint uniformNameMaxLength = 0;
1641 gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS, &uniforms);
1642 gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformNameMaxLength);
1644 // we now need the type info to switch between fGetUniformfv and fGetUniformiv
1645 // the only way to get that is to iterate through all active uniforms by index until
1646 // one matches the given uniform location.
1647 GLenum uniformType = 0;
1648 nsAutoArrayPtr<GLchar> uniformName(new GLchar[uniformNameMaxLength]);
1649 // this buffer has 16 more bytes to be able to store [index] at the end.
1650 nsAutoArrayPtr<GLchar> uniformNameBracketIndex(new GLchar[uniformNameMaxLength + 16]);
1652 GLint index;
1653 for (index = 0; index < uniforms; ++index) {
1654 GLsizei length;
1655 GLint size;
1656 gl->fGetActiveUniform(progname, index, uniformNameMaxLength, &length,
1657 &size, &uniformType, uniformName);
1658 if (gl->fGetUniformLocation(progname, uniformName) == location->Location())
1659 break;
1661 // now we handle the case of array uniforms. In that case, fGetActiveUniform returned as 'size'
1662 // the biggest index used plus one, so we need to loop over that. The 0 index has already been handled above,
1663 // so we can start at one. For each index, we construct the string uniformName + "[" + index + "]".
1664 if (size > 1) {
1665 bool found_it = false;
1666 if (uniformName[length - 1] == ']') { // if uniformName ends in [0]
1667 // remove the [0] at the end
1668 length -= 3;
1669 uniformName[length] = 0;
1670 }
1671 for (GLint arrayIndex = 1; arrayIndex < size; arrayIndex++) {
1672 sprintf(uniformNameBracketIndex.get(), "%s[%d]", uniformName.get(), arrayIndex);
1673 if (gl->fGetUniformLocation(progname, uniformNameBracketIndex) == location->Location()) {
1674 found_it = true;
1675 break;
1676 }
1677 }
1678 if (found_it) break;
1679 }
1680 }
1682 if (index == uniforms) {
1683 GenerateWarning("getUniform: internal error: hit an OpenGL driver bug");
1684 return JS::NullValue();
1685 }
1687 GLenum baseType;
1688 GLint unitSize;
1689 if (!BaseTypeAndSizeFromUniformType(uniformType, &baseType, &unitSize)) {
1690 GenerateWarning("getUniform: internal error: unknown uniform type 0x%x", uniformType);
1691 return JS::NullValue();
1692 }
1694 // this should never happen
1695 if (unitSize > 16) {
1696 GenerateWarning("getUniform: internal error: unexpected uniform unit size %d", unitSize);
1697 return JS::NullValue();
1698 }
1700 if (baseType == LOCAL_GL_FLOAT) {
1701 GLfloat fv[16] = { GLfloat(0) };
1702 gl->fGetUniformfv(progname, location->Location(), fv);
1703 if (unitSize == 1) {
1704 return JS::DoubleValue(fv[0]);
1705 } else {
1706 JSObject* obj = Float32Array::Create(cx, this, unitSize, fv);
1707 if (!obj) {
1708 ErrorOutOfMemory("getUniform: out of memory");
1709 return JS::NullValue();
1710 }
1711 return JS::ObjectOrNullValue(obj);
1712 }
1713 } else if (baseType == LOCAL_GL_INT) {
1714 GLint iv[16] = { 0 };
1715 gl->fGetUniformiv(progname, location->Location(), iv);
1716 if (unitSize == 1) {
1717 return JS::Int32Value(iv[0]);
1718 } else {
1719 JSObject* obj = Int32Array::Create(cx, this, unitSize, iv);
1720 if (!obj) {
1721 ErrorOutOfMemory("getUniform: out of memory");
1722 return JS::NullValue();
1723 }
1724 return JS::ObjectOrNullValue(obj);
1725 }
1726 } else if (baseType == LOCAL_GL_BOOL) {
1727 GLint iv[16] = { 0 };
1728 gl->fGetUniformiv(progname, location->Location(), iv);
1729 if (unitSize == 1) {
1730 return JS::BooleanValue(iv[0] ? true : false);
1731 } else {
1732 bool uv[16];
1733 for (int k = 0; k < unitSize; k++)
1734 uv[k] = iv[k];
1735 JS::Rooted<JS::Value> val(cx);
1736 // Be careful: we don't want to convert all of |uv|!
1737 if (!ToJSValue(cx, uv, unitSize, &val)) {
1738 ErrorOutOfMemory("getUniform: out of memory");
1739 return JS::NullValue();
1740 }
1741 return val;
1742 }
1743 }
1745 // Else preserving behavior, but I'm not sure this is correct per spec
1746 return JS::UndefinedValue();
1747 }
1749 already_AddRefed<WebGLUniformLocation>
1750 WebGLContext::GetUniformLocation(WebGLProgram *prog, const nsAString& name)
1751 {
1752 if (IsContextLost())
1753 return nullptr;
1755 if (!ValidateObject("getUniformLocation: program", prog))
1756 return nullptr;
1758 if (!ValidateGLSLVariableName(name, "getUniformLocation"))
1759 return nullptr;
1761 NS_LossyConvertUTF16toASCII cname(name);
1762 nsCString mappedName;
1763 prog->MapIdentifier(cname, &mappedName);
1765 GLuint progname = prog->GLName();
1766 MakeContextCurrent();
1767 GLint intlocation = gl->fGetUniformLocation(progname, mappedName.get());
1769 nsRefPtr<WebGLUniformLocation> loc;
1770 if (intlocation >= 0) {
1771 WebGLUniformInfo info = prog->GetUniformInfoForMappedIdentifier(mappedName);
1772 loc = new WebGLUniformLocation(this,
1773 prog,
1774 intlocation,
1775 info);
1776 }
1777 return loc.forget();
1778 }
1780 void
1781 WebGLContext::Hint(GLenum target, GLenum mode)
1782 {
1783 if (IsContextLost())
1784 return;
1786 bool isValid = false;
1788 switch (target) {
1789 case LOCAL_GL_GENERATE_MIPMAP_HINT:
1790 isValid = true;
1791 break;
1792 case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
1793 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
1794 isValid = true;
1795 break;
1796 }
1798 if (!isValid)
1799 return ErrorInvalidEnum("hint: invalid hint");
1801 MakeContextCurrent();
1802 gl->fHint(target, mode);
1803 }
1805 bool
1806 WebGLContext::IsFramebuffer(WebGLFramebuffer *fb)
1807 {
1808 if (IsContextLost())
1809 return false;
1811 return ValidateObjectAllowDeleted("isFramebuffer", fb) &&
1812 !fb->IsDeleted() &&
1813 fb->HasEverBeenBound();
1814 }
1816 bool
1817 WebGLContext::IsProgram(WebGLProgram *prog)
1818 {
1819 if (IsContextLost())
1820 return false;
1822 return ValidateObjectAllowDeleted("isProgram", prog) && !prog->IsDeleted();
1823 }
1825 bool
1826 WebGLContext::IsRenderbuffer(WebGLRenderbuffer *rb)
1827 {
1828 if (IsContextLost())
1829 return false;
1831 return ValidateObjectAllowDeleted("isRenderBuffer", rb) &&
1832 !rb->IsDeleted() &&
1833 rb->HasEverBeenBound();
1834 }
1836 bool
1837 WebGLContext::IsShader(WebGLShader *shader)
1838 {
1839 if (IsContextLost())
1840 return false;
1842 return ValidateObjectAllowDeleted("isShader", shader) &&
1843 !shader->IsDeleted();
1844 }
1846 bool
1847 WebGLContext::IsTexture(WebGLTexture *tex)
1848 {
1849 if (IsContextLost())
1850 return false;
1852 return ValidateObjectAllowDeleted("isTexture", tex) &&
1853 !tex->IsDeleted() &&
1854 tex->HasEverBeenBound();
1855 }
1857 // Try to bind an attribute that is an array to location 0:
1858 bool WebGLContext::BindArrayAttribToLocation0(WebGLProgram *program)
1859 {
1860 if (mBoundVertexArray->IsAttribArrayEnabled(0)) {
1861 return false;
1862 }
1864 GLint leastArrayLocation = -1;
1866 std::map<GLint, nsCString>::iterator itr;
1867 for (itr = program->mActiveAttribMap.begin();
1868 itr != program->mActiveAttribMap.end();
1869 itr++) {
1870 int32_t index = itr->first;
1871 if (mBoundVertexArray->IsAttribArrayEnabled(index) &&
1872 index < leastArrayLocation)
1873 {
1874 leastArrayLocation = index;
1875 }
1876 }
1878 if (leastArrayLocation > 0) {
1879 nsCString& attrName = program->mActiveAttribMap.find(leastArrayLocation)->second;
1880 const char* attrNameCStr = attrName.get();
1881 gl->fBindAttribLocation(program->GLName(), 0, attrNameCStr);
1882 return true;
1883 }
1884 return false;
1885 }
1887 void
1888 WebGLContext::LinkProgram(WebGLProgram *program)
1889 {
1890 if (IsContextLost())
1891 return;
1893 if (!ValidateObject("linkProgram", program))
1894 return;
1896 InvalidateBufferFetching(); // we do it early in this function
1897 // as some of the validation below changes program state
1899 GLuint progname = program->GLName();
1901 if (!program->NextGeneration()) {
1902 // XXX throw?
1903 return;
1904 }
1906 if (!program->HasBothShaderTypesAttached()) {
1907 GenerateWarning("linkProgram: this program doesn't have both a vertex shader"
1908 " and a fragment shader");
1909 program->SetLinkStatus(false);
1910 return;
1911 }
1913 // bug 777028
1914 // Mesa can't handle more than 16 samplers per program, counting each array entry.
1915 if (gl->WorkAroundDriverBugs() &&
1916 mIsMesa &&
1917 program->UpperBoundNumSamplerUniforms() > 16)
1918 {
1919 GenerateWarning("Programs with more than 16 samplers are disallowed on Mesa drivers " "to avoid a Mesa crasher.");
1920 program->SetLinkStatus(false);
1921 return;
1922 }
1924 bool updateInfoSucceeded = false;
1925 GLint ok = 0;
1926 if (gl->WorkAroundDriverBugs() &&
1927 program->HasBadShaderAttached())
1928 {
1929 // it's a common driver bug, caught by program-test.html, that linkProgram doesn't
1930 // correctly preserve the state of an in-use program that has been attached a bad shader
1931 // see bug 777883
1932 ok = false;
1933 } else {
1934 MakeContextCurrent();
1935 gl->fLinkProgram(progname);
1936 gl->fGetProgramiv(progname, LOCAL_GL_LINK_STATUS, &ok);
1938 if (ok) {
1939 updateInfoSucceeded = program->UpdateInfo();
1940 program->SetLinkStatus(updateInfoSucceeded);
1942 if (BindArrayAttribToLocation0(program)) {
1943 GenerateWarning("linkProgram: relinking program to make attrib0 an "
1944 "array.");
1945 gl->fLinkProgram(progname);
1946 gl->fGetProgramiv(progname, LOCAL_GL_LINK_STATUS, &ok);
1947 if (ok) {
1948 updateInfoSucceeded = program->UpdateInfo();
1949 program->SetLinkStatus(updateInfoSucceeded);
1950 }
1951 }
1952 }
1953 }
1955 if (ok) {
1956 // Bug 750527
1957 if (gl->WorkAroundDriverBugs() &&
1958 updateInfoSucceeded &&
1959 gl->Vendor() == gl::GLVendor::NVIDIA)
1960 {
1961 if (program == mCurrentProgram)
1962 gl->fUseProgram(progname);
1963 }
1964 } else {
1965 program->SetLinkStatus(false);
1967 if (ShouldGenerateWarnings()) {
1969 // report shader/program infoLogs as warnings.
1970 // note that shader compilation errors can be deferred to linkProgram,
1971 // which is why we can't do anything in compileShader. In practice we could
1972 // report in compileShader the translation errors generated by ANGLE,
1973 // but it seems saner to keep a single way of obtaining shader infologs.
1975 nsAutoCString log;
1977 bool alreadyReportedShaderInfoLog = false;
1979 for (size_t i = 0; i < program->AttachedShaders().Length(); i++) {
1981 WebGLShader* shader = program->AttachedShaders()[i];
1983 if (shader->CompileStatus())
1984 continue;
1986 const char *shaderTypeName = nullptr;
1987 if (shader->ShaderType() == LOCAL_GL_VERTEX_SHADER) {
1988 shaderTypeName = "vertex";
1989 } else if (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) {
1990 shaderTypeName = "fragment";
1991 } else {
1992 // should have been validated earlier
1993 MOZ_ASSERT(false);
1994 shaderTypeName = "<unknown>";
1995 }
1997 GetShaderInfoLog(shader, log);
1999 GenerateWarning("linkProgram: a %s shader used in this program failed to "
2000 "compile, with this log:\n%s\n",
2001 shaderTypeName,
2002 log.get());
2003 alreadyReportedShaderInfoLog = true;
2004 }
2006 if (!alreadyReportedShaderInfoLog) {
2007 GetProgramInfoLog(program, log);
2008 if (!log.IsEmpty()) {
2009 GenerateWarning("linkProgram failed, with this log:\n%s\n",
2010 log.get());
2011 }
2012 }
2013 }
2014 }
2015 }
2017 void
2018 WebGLContext::PixelStorei(GLenum pname, GLint param)
2019 {
2020 if (IsContextLost())
2021 return;
2023 switch (pname) {
2024 case UNPACK_FLIP_Y_WEBGL:
2025 mPixelStoreFlipY = (param != 0);
2026 break;
2027 case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
2028 mPixelStorePremultiplyAlpha = (param != 0);
2029 break;
2030 case UNPACK_COLORSPACE_CONVERSION_WEBGL:
2031 if (param == LOCAL_GL_NONE || param == BROWSER_DEFAULT_WEBGL)
2032 mPixelStoreColorspaceConversion = param;
2033 else
2034 return ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter", param);
2035 break;
2036 case LOCAL_GL_PACK_ALIGNMENT:
2037 case LOCAL_GL_UNPACK_ALIGNMENT:
2038 if (param != 1 &&
2039 param != 2 &&
2040 param != 4 &&
2041 param != 8)
2042 return ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value");
2043 if (pname == LOCAL_GL_PACK_ALIGNMENT)
2044 mPixelStorePackAlignment = param;
2045 else if (pname == LOCAL_GL_UNPACK_ALIGNMENT)
2046 mPixelStoreUnpackAlignment = param;
2047 MakeContextCurrent();
2048 gl->fPixelStorei(pname, param);
2049 break;
2050 default:
2051 return ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
2052 }
2053 }
2055 void
2056 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
2057 GLsizei height, GLenum format,
2058 GLenum type, const Nullable<ArrayBufferView> &pixels,
2059 ErrorResult& rv)
2060 {
2061 if (IsContextLost())
2062 return;
2064 if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) {
2065 GenerateWarning("readPixels: Not allowed");
2066 return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
2067 }
2069 if (width < 0 || height < 0)
2070 return ErrorInvalidValue("readPixels: negative size passed");
2072 if (pixels.IsNull())
2073 return ErrorInvalidValue("readPixels: null destination buffer");
2075 const WebGLRectangleObject* framebufferRect = CurValidFBRectObject();
2076 GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0;
2077 GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0;
2079 uint32_t channels = 0;
2081 // Check the format param
2082 switch (format) {
2083 case LOCAL_GL_ALPHA:
2084 channels = 1;
2085 break;
2086 case LOCAL_GL_RGB:
2087 channels = 3;
2088 break;
2089 case LOCAL_GL_RGBA:
2090 channels = 4;
2091 break;
2092 default:
2093 return ErrorInvalidEnum("readPixels: Bad format");
2094 }
2096 uint32_t bytesPerPixel = 0;
2097 int requiredDataType = 0;
2099 // Check the type param
2100 bool isReadTypeValid = false;
2101 bool isReadTypeFloat = false;
2102 switch (type) {
2103 case LOCAL_GL_UNSIGNED_BYTE:
2104 isReadTypeValid = true;
2105 bytesPerPixel = 1*channels;
2106 requiredDataType = js::ArrayBufferView::TYPE_UINT8;
2107 break;
2108 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
2109 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
2110 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
2111 isReadTypeValid = true;
2112 bytesPerPixel = 2;
2113 requiredDataType = js::ArrayBufferView::TYPE_UINT16;
2114 break;
2115 case LOCAL_GL_FLOAT:
2116 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float) ||
2117 IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float))
2118 {
2119 isReadTypeValid = true;
2120 isReadTypeFloat = true;
2121 bytesPerPixel = 4*channels;
2122 requiredDataType = js::ArrayBufferView::TYPE_FLOAT32;
2123 }
2124 break;
2125 }
2126 if (!isReadTypeValid)
2127 return ErrorInvalidEnum("readPixels: Bad type", type);
2129 const ArrayBufferView& pixbuf = pixels.Value();
2130 int dataType = JS_GetArrayBufferViewType(pixbuf.Obj());
2132 // Check the pixels param type
2133 if (dataType != requiredDataType)
2134 return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
2136 // Check the pixels param size
2137 CheckedUint32 checked_neededByteLength =
2138 GetImageSize(height, width, bytesPerPixel, mPixelStorePackAlignment);
2140 CheckedUint32 checked_plainRowSize = CheckedUint32(width) * bytesPerPixel;
2142 CheckedUint32 checked_alignedRowSize =
2143 RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment);
2145 if (!checked_neededByteLength.isValid())
2146 return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size");
2148 // Compute length and data. Don't reenter after this point, lest the
2149 // precomputed go out of sync with the instant length/data.
2150 pixbuf.ComputeLengthAndData();
2152 uint32_t dataByteLen = pixbuf.Length();
2153 if (checked_neededByteLength.value() > dataByteLen)
2154 return ErrorInvalidOperation("readPixels: buffer too small");
2156 void* data = pixbuf.Data();
2157 if (!data) {
2158 ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
2159 return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
2160 }
2162 bool isSourceTypeFloat = false;
2163 if (mBoundFramebuffer &&
2164 mBoundFramebuffer->ColorAttachmentCount() &&
2165 mBoundFramebuffer->ColorAttachment(0).IsDefined())
2166 {
2167 isSourceTypeFloat = mBoundFramebuffer->ColorAttachment(0).IsReadableFloat();
2168 }
2170 if (isReadTypeFloat != isSourceTypeFloat)
2171 return ErrorInvalidOperation("readPixels: Invalid type floatness");
2173 // Check the format and type params to assure they are an acceptable pair (as per spec)
2174 switch (format) {
2175 case LOCAL_GL_RGBA: {
2176 switch (type) {
2177 case LOCAL_GL_UNSIGNED_BYTE:
2178 break;
2179 case LOCAL_GL_FLOAT:
2180 break;
2181 default:
2182 return ErrorInvalidOperation("readPixels: Invalid format/type pair");
2183 }
2184 break;
2185 }
2186 default:
2187 return ErrorInvalidOperation("readPixels: Invalid format/type pair");
2188 }
2190 MakeContextCurrent();
2192 if (mBoundFramebuffer) {
2193 // prevent readback of arbitrary video memory through uninitialized renderbuffers!
2194 if (!mBoundFramebuffer->CheckAndInitializeAttachments())
2195 return ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer");
2197 GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
2198 if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
2199 return ErrorInvalidOperation("readPixels: Read source attachment doesn't have the"
2200 " correct color/depth/stencil type.");
2201 }
2202 } else {
2203 ClearBackbufferIfNeeded();
2204 }
2205 // Now that the errors are out of the way, on to actually reading
2207 // If we won't be reading any pixels anyways, just skip the actual reading
2208 if (width == 0 || height == 0)
2209 return DummyFramebufferOperation("readPixels");
2211 if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
2212 // the easy case: we're not reading out-of-range pixels
2213 gl->fReadPixels(x, y, width, height, format, type, data);
2214 } else {
2215 // the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part
2216 // of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL
2217 // to do that for us, because passing out of range parameters to a buggy OpenGL implementation
2218 // could conceivably allow to read memory we shouldn't be allowed to read. So we manually initialize
2219 // the buffer to zero and compute the parameters to pass to OpenGL. We have to use an intermediate buffer
2220 // to accomodate the potentially different strides (widths).
2222 // Zero the whole pixel dest area in the destination buffer.
2223 memset(data, 0, checked_neededByteLength.value());
2225 if ( x >= framebufferWidth
2226 || x+width <= 0
2227 || y >= framebufferHeight
2228 || y+height <= 0)
2229 {
2230 // we are completely outside of range, can exit now with buffer filled with zeros
2231 return DummyFramebufferOperation("readPixels");
2232 }
2234 // compute the parameters of the subrect we're actually going to call glReadPixels on
2235 GLint subrect_x = std::max(x, 0);
2236 GLint subrect_end_x = std::min(x+width, framebufferWidth);
2237 GLsizei subrect_width = subrect_end_x - subrect_x;
2239 GLint subrect_y = std::max(y, 0);
2240 GLint subrect_end_y = std::min(y+height, framebufferHeight);
2241 GLsizei subrect_height = subrect_end_y - subrect_y;
2243 if (subrect_width < 0 || subrect_height < 0 ||
2244 subrect_width > width || subrect_height > height)
2245 return ErrorInvalidOperation("readPixels: integer overflow computing clipped rect size");
2247 // now we know that subrect_width is in the [0..width] interval, and same for heights.
2249 // now, same computation as above to find the size of the intermediate buffer to allocate for the subrect
2250 // no need to check again for integer overflow here, since we already know the sizes aren't greater than before
2251 uint32_t subrect_plainRowSize = subrect_width * bytesPerPixel;
2252 // There are checks above to ensure that this doesn't overflow.
2253 uint32_t subrect_alignedRowSize =
2254 RoundedToNextMultipleOf(subrect_plainRowSize, mPixelStorePackAlignment).value();
2255 uint32_t subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize;
2257 // create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer
2258 GLubyte *subrect_data = new GLubyte[subrect_byteLength];
2259 gl->fReadPixels(subrect_x, subrect_y, subrect_width, subrect_height, format, type, subrect_data);
2261 // notice that this for loop terminates because we already checked that subrect_height is at most height
2262 for (GLint y_inside_subrect = 0; y_inside_subrect < subrect_height; ++y_inside_subrect) {
2263 GLint subrect_x_in_dest_buffer = subrect_x - x;
2264 GLint subrect_y_in_dest_buffer = subrect_y - y;
2265 memcpy(static_cast<GLubyte*>(data)
2266 + checked_alignedRowSize.value() * (subrect_y_in_dest_buffer + y_inside_subrect)
2267 + bytesPerPixel * subrect_x_in_dest_buffer, // destination
2268 subrect_data + subrect_alignedRowSize * y_inside_subrect, // source
2269 subrect_plainRowSize); // size
2270 }
2271 delete [] subrect_data;
2272 }
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)
2278 {
2279 bool needAlphaFixup;
2280 if (mBoundFramebuffer) {
2281 needAlphaFixup = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
2282 } else {
2283 needAlphaFixup = gl->GetPixelFormat().alpha == 0;
2284 }
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();
2293 }
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;
2308 }
2310 row += checked_alignedRowSize.value();
2311 }
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;
2322 }
2324 row += checked_alignedRowSize.value();
2325 }
2326 } else {
2327 NS_WARNING("Unhandled case, how'd we get here?");
2328 return rv.Throw(NS_ERROR_FAILURE);
2329 }
2330 }
2331 }
2332 }
2334 void
2335 WebGLContext::RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
2336 {
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;
2386 }
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;
2394 }
2395 default:
2396 return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", internalformat);
2397 }
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;
2413 }
2414 } else {
2415 mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height);
2416 }
2418 mBoundRenderbuffer->SetInternalFormat(internalformat);
2419 mBoundRenderbuffer->SetInternalFormatForGL(internalformatForGL);
2420 mBoundRenderbuffer->setDimensions(width, height);
2421 mBoundRenderbuffer->SetImageDataStatus(WebGLImageDataStatus::UninitializedImageData);
2422 }
2424 void
2425 WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height)
2426 {
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);
2435 }
2437 void
2438 WebGLContext::StencilFunc(GLenum func, GLint ref, GLuint mask)
2439 {
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);
2453 }
2455 void
2456 WebGLContext::StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
2457 {
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;
2480 }
2482 MakeContextCurrent();
2483 gl->fStencilFuncSeparate(face, func, ref, mask);
2484 }
2486 void
2487 WebGLContext::StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
2488 {
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);
2499 }
2501 void
2502 WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
2503 {
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);
2515 }
2517 nsresult
2518 WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
2519 RefPtr<DataSourceSurface>& imageOut, WebGLTexelFormat *format)
2520 {
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;
2529 }
2531 if (!mPixelStorePremultiplyAlpha && res.mIsPremultiplied) {
2532 data = gfxUtils::UnpremultiplyDataSurface(data);
2533 }
2535 // We disallow loading cross-domain images and videos that have not been validated
2536 // with CORS as WebGL textures. The reason for doing that is that timing
2537 // attacks on WebGL shaders are able to retrieve approximations of the
2538 // pixel values in WebGL textures; see bug 655987.
2539 //
2540 // To prevent a loophole where a Canvas2D would be used as a proxy to load
2541 // cross-domain textures, we also disallow loading textures from write-only
2542 // Canvas2D's.
2544 // part 1: check that the DOM element is same-origin, or has otherwise been
2545 // validated for cross-domain use.
2546 if (!res.mCORSUsed) {
2547 bool subsumes;
2548 nsresult rv = mCanvasElement->NodePrincipal()->Subsumes(res.mPrincipal, &subsumes);
2549 if (NS_FAILED(rv) || !subsumes) {
2550 GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. "
2551 "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
2552 return NS_ERROR_DOM_SECURITY_ERR;
2553 }
2554 }
2556 // part 2: if the DOM element is write-only, it might contain
2557 // cross-domain image data.
2558 if (res.mIsWriteOnly) {
2559 GenerateWarning("The canvas used as source for texImage2D here is tainted (write-only). It is forbidden "
2560 "to load a WebGL texture from a tainted canvas. A Canvas becomes tainted for example "
2561 "when a cross-domain image is drawn on it. "
2562 "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
2563 return NS_ERROR_DOM_SECURITY_ERR;
2564 }
2566 // End of security checks, now we should be safe regarding cross-domain images
2567 // Notice that there is never a need to mark the WebGL canvas as write-only, since we reject write-only/cross-domain
2568 // texture sources in the first place.
2570 switch (data->GetFormat()) {
2571 case SurfaceFormat::B8G8R8A8:
2572 *format = WebGLTexelFormat::BGRA8; // careful, our ARGB means BGRA
2573 break;
2574 case SurfaceFormat::B8G8R8X8:
2575 *format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8.
2576 break;
2577 case SurfaceFormat::A8:
2578 *format = WebGLTexelFormat::A8;
2579 break;
2580 case SurfaceFormat::R5G6B5:
2581 *format = WebGLTexelFormat::RGB565;
2582 break;
2583 default:
2584 NS_ASSERTION(false, "Unsupported image format. Unimplemented.");
2585 return NS_ERROR_NOT_IMPLEMENTED;
2586 }
2588 imageOut = data;
2590 return NS_OK;
2591 }
2595 void
2596 WebGLContext::Uniform1i(WebGLUniformLocation *location_object, GLint a1)
2597 {
2598 GLint location;
2599 if (!ValidateUniformSetter("Uniform1i", location_object, location))
2600 return;
2602 // Only uniform1i can take sampler settings.
2603 if (!ValidateSamplerUniformSetter("Uniform1i", location_object, a1))
2604 return;
2606 MakeContextCurrent();
2607 gl->fUniform1i(location, a1);
2608 }
2610 void
2611 WebGLContext::Uniform2i(WebGLUniformLocation *location_object, GLint a1,
2612 GLint a2)
2613 {
2614 GLint location;
2615 if (!ValidateUniformSetter("Uniform2i", location_object, location))
2616 return;
2618 MakeContextCurrent();
2619 gl->fUniform2i(location, a1, a2);
2620 }
2622 void
2623 WebGLContext::Uniform3i(WebGLUniformLocation *location_object, GLint a1,
2624 GLint a2, GLint a3)
2625 {
2626 GLint location;
2627 if (!ValidateUniformSetter("Uniform3i", location_object, location))
2628 return;
2630 MakeContextCurrent();
2631 gl->fUniform3i(location, a1, a2, a3);
2632 }
2634 void
2635 WebGLContext::Uniform4i(WebGLUniformLocation *location_object, GLint a1,
2636 GLint a2, GLint a3, GLint a4)
2637 {
2638 GLint location;
2639 if (!ValidateUniformSetter("Uniform4i", location_object, location))
2640 return;
2642 MakeContextCurrent();
2643 gl->fUniform4i(location, a1, a2, a3, a4);
2644 }
2646 void
2647 WebGLContext::Uniform1f(WebGLUniformLocation *location_object, GLfloat a1)
2648 {
2649 GLint location;
2650 if (!ValidateUniformSetter("Uniform1f", location_object, location))
2651 return;
2652 MakeContextCurrent();
2653 gl->fUniform1f(location, a1);
2654 }
2656 void
2657 WebGLContext::Uniform2f(WebGLUniformLocation *location_object, GLfloat a1,
2658 GLfloat a2)
2659 {
2660 GLint location;
2661 if (!ValidateUniformSetter("Uniform2f", location_object, location))
2662 return;
2663 MakeContextCurrent();
2664 gl->fUniform2f(location, a1, a2);
2665 }
2667 void
2668 WebGLContext::Uniform3f(WebGLUniformLocation *location_object, GLfloat a1,
2669 GLfloat a2, GLfloat a3)
2670 {
2671 GLint location;
2672 if (!ValidateUniformSetter("Uniform3f", location_object, location))
2673 return;
2674 MakeContextCurrent();
2675 gl->fUniform3f(location, a1, a2, a3);
2676 }
2678 void
2679 WebGLContext::Uniform4f(WebGLUniformLocation *location_object, GLfloat a1,
2680 GLfloat a2, GLfloat a3, GLfloat a4)
2681 {
2682 GLint location;
2683 if (!ValidateUniformSetter("Uniform4f", location_object, location))
2684 return;
2685 MakeContextCurrent();
2686 gl->fUniform4f(location, a1, a2, a3, a4);
2687 }
2689 void
2690 WebGLContext::Uniform1iv_base(WebGLUniformLocation *location_object,
2691 uint32_t arrayLength, const GLint* data)
2692 {
2693 uint32_t numElementsToUpload;
2694 GLint location;
2695 if (!ValidateUniformArraySetter("Uniform1iv", 1, location_object, location,
2696 numElementsToUpload, arrayLength)) {
2697 return;
2698 }
2700 if (!ValidateSamplerUniformSetter("Uniform1iv", location_object, data[0]))
2701 return;
2703 MakeContextCurrent();
2704 gl->fUniform1iv(location, numElementsToUpload, data);
2705 }
2707 void
2708 WebGLContext::Uniform2iv_base(WebGLUniformLocation *location_object,
2709 uint32_t arrayLength, const GLint* data)
2710 {
2711 uint32_t numElementsToUpload;
2712 GLint location;
2713 if (!ValidateUniformArraySetter("Uniform2iv", 2, location_object, location,
2714 numElementsToUpload, arrayLength)) {
2715 return;
2716 }
2718 if (!ValidateSamplerUniformSetter("Uniform2iv", location_object, data[0]) ||
2719 !ValidateSamplerUniformSetter("Uniform2iv", location_object, data[1]))
2720 {
2721 return;
2722 }
2724 MakeContextCurrent();
2725 gl->fUniform2iv(location, numElementsToUpload, data);
2726 }
2728 void
2729 WebGLContext::Uniform3iv_base(WebGLUniformLocation *location_object,
2730 uint32_t arrayLength, const GLint* data)
2731 {
2732 uint32_t numElementsToUpload;
2733 GLint location;
2734 if (!ValidateUniformArraySetter("Uniform3iv", 3, location_object, location,
2735 numElementsToUpload, arrayLength)) {
2736 return;
2737 }
2739 if (!ValidateSamplerUniformSetter("Uniform3iv", location_object, data[0]) ||
2740 !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[1]) ||
2741 !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[2]))
2742 {
2743 return;
2744 }
2746 MakeContextCurrent();
2747 gl->fUniform3iv(location, numElementsToUpload, data);
2748 }
2750 void
2751 WebGLContext::Uniform4iv_base(WebGLUniformLocation *location_object,
2752 uint32_t arrayLength, const GLint* data)
2753 {
2754 uint32_t numElementsToUpload;
2755 GLint location;
2756 if (!ValidateUniformArraySetter("Uniform4iv", 4, location_object, location,
2757 numElementsToUpload, arrayLength)) {
2758 return;
2759 }
2761 if (!ValidateSamplerUniformSetter("Uniform4iv", location_object, data[0]) ||
2762 !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[1]) ||
2763 !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[2]) ||
2764 !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[3]))
2765 {
2766 return;
2767 }
2769 MakeContextCurrent();
2770 gl->fUniform4iv(location, numElementsToUpload, data);
2771 }
2773 void
2774 WebGLContext::Uniform1fv_base(WebGLUniformLocation *location_object,
2775 uint32_t arrayLength, const GLfloat* data)
2776 {
2777 uint32_t numElementsToUpload;
2778 GLint location;
2779 if (!ValidateUniformArraySetter("Uniform1fv", 1, location_object, location,
2780 numElementsToUpload, arrayLength)) {
2781 return;
2782 }
2783 MakeContextCurrent();
2784 gl->fUniform1fv(location, numElementsToUpload, data);
2785 }
2787 void
2788 WebGLContext::Uniform2fv_base(WebGLUniformLocation *location_object,
2789 uint32_t arrayLength, const GLfloat* data)
2790 {
2791 uint32_t numElementsToUpload;
2792 GLint location;
2793 if (!ValidateUniformArraySetter("Uniform2fv", 2, location_object, location,
2794 numElementsToUpload, arrayLength)) {
2795 return;
2796 }
2797 MakeContextCurrent();
2798 gl->fUniform2fv(location, numElementsToUpload, data);
2799 }
2801 void
2802 WebGLContext::Uniform3fv_base(WebGLUniformLocation *location_object,
2803 uint32_t arrayLength, const GLfloat* data)
2804 {
2805 uint32_t numElementsToUpload;
2806 GLint location;
2807 if (!ValidateUniformArraySetter("Uniform3fv", 3, location_object, location,
2808 numElementsToUpload, arrayLength)) {
2809 return;
2810 }
2811 MakeContextCurrent();
2812 gl->fUniform3fv(location, numElementsToUpload, data);
2813 }
2815 void
2816 WebGLContext::Uniform4fv_base(WebGLUniformLocation *location_object,
2817 uint32_t arrayLength, const GLfloat* data)
2818 {
2819 uint32_t numElementsToUpload;
2820 GLint location;
2821 if (!ValidateUniformArraySetter("Uniform4fv", 4, location_object, location,
2822 numElementsToUpload, arrayLength)) {
2823 return;
2824 }
2825 MakeContextCurrent();
2826 gl->fUniform4fv(location, numElementsToUpload, data);
2827 }
2829 void
2830 WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* location_object,
2831 WebGLboolean aTranspose, uint32_t arrayLength,
2832 const float* data)
2833 {
2834 uint32_t numElementsToUpload;
2835 GLint location;
2836 if (!ValidateUniformMatrixArraySetter("UniformMatrix2fv", 2, location_object, location,
2837 numElementsToUpload, arrayLength, aTranspose)) {
2838 return;
2839 }
2840 MakeContextCurrent();
2841 gl->fUniformMatrix2fv(location, numElementsToUpload, false, data);
2842 }
2844 void
2845 WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* location_object,
2846 WebGLboolean aTranspose, uint32_t arrayLength,
2847 const float* data)
2848 {
2849 uint32_t numElementsToUpload;
2850 GLint location;
2851 if (!ValidateUniformMatrixArraySetter("UniformMatrix3fv", 3, location_object, location,
2852 numElementsToUpload, arrayLength, aTranspose)) {
2853 return;
2854 }
2855 MakeContextCurrent();
2856 gl->fUniformMatrix3fv(location, numElementsToUpload, false, data);
2857 }
2859 void
2860 WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* location_object,
2861 WebGLboolean aTranspose, uint32_t arrayLength,
2862 const float* data)
2863 {
2864 uint32_t numElementsToUpload;
2865 GLint location;
2866 if (!ValidateUniformMatrixArraySetter("UniformMatrix4fv", 4, location_object, location,
2867 numElementsToUpload, arrayLength, aTranspose)) {
2868 return;
2869 }
2870 MakeContextCurrent();
2871 gl->fUniformMatrix4fv(location, numElementsToUpload, false, data);
2872 }
2874 void
2875 WebGLContext::UseProgram(WebGLProgram *prog)
2876 {
2877 if (IsContextLost())
2878 return;
2880 if (!ValidateObjectAllowNull("useProgram", prog))
2881 return;
2883 MakeContextCurrent();
2885 InvalidateBufferFetching();
2887 GLuint progname = prog ? prog->GLName() : 0;
2889 if (prog && !prog->LinkStatus())
2890 return ErrorInvalidOperation("useProgram: program was not linked successfully");
2892 gl->fUseProgram(progname);
2894 mCurrentProgram = prog;
2895 }
2897 void
2898 WebGLContext::ValidateProgram(WebGLProgram *prog)
2899 {
2900 if (IsContextLost())
2901 return;
2903 if (!ValidateObject("validateProgram", prog))
2904 return;
2906 MakeContextCurrent();
2908 #ifdef XP_MACOSX
2909 // see bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed with Mac OS 10.6.7
2910 if (gl->WorkAroundDriverBugs()) {
2911 GenerateWarning("validateProgram: implemented as a no-operation on Mac to work around crashes");
2912 return;
2913 }
2914 #endif
2916 GLuint progname = prog->GLName();
2917 gl->fValidateProgram(progname);
2918 }
2920 already_AddRefed<WebGLFramebuffer>
2921 WebGLContext::CreateFramebuffer()
2922 {
2923 if (IsContextLost())
2924 return nullptr;
2925 nsRefPtr<WebGLFramebuffer> globj = new WebGLFramebuffer(this);
2926 return globj.forget();
2927 }
2929 already_AddRefed<WebGLRenderbuffer>
2930 WebGLContext::CreateRenderbuffer()
2931 {
2932 if (IsContextLost())
2933 return nullptr;
2934 nsRefPtr<WebGLRenderbuffer> globj = new WebGLRenderbuffer(this);
2935 return globj.forget();
2936 }
2938 void
2939 WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
2940 {
2941 if (IsContextLost())
2942 return;
2944 if (width < 0 || height < 0)
2945 return ErrorInvalidValue("viewport: negative size");
2947 MakeContextCurrent();
2948 gl->fViewport(x, y, width, height);
2950 mViewportX = x;
2951 mViewportY = y;
2952 mViewportWidth = width;
2953 mViewportHeight = height;
2954 }
2956 void
2957 WebGLContext::CompileShader(WebGLShader *shader)
2958 {
2959 if (IsContextLost())
2960 return;
2962 if (!ValidateObject("compileShader", shader))
2963 return;
2965 GLuint shadername = shader->GLName();
2967 shader->SetCompileStatus(false);
2969 MakeContextCurrent();
2971 ShShaderOutput targetShaderSourceLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT;
2972 bool useShaderSourceTranslation = true;
2974 if (shader->NeedsTranslation() && mShaderValidation) {
2975 ShHandle compiler = 0;
2976 ShBuiltInResources resources;
2977 memset(&resources, 0, sizeof(ShBuiltInResources));
2979 resources.MaxVertexAttribs = mGLMaxVertexAttribs;
2980 resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors;
2981 resources.MaxVaryingVectors = mGLMaxVaryingVectors;
2982 resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
2983 resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits;
2984 resources.MaxTextureImageUnits = mGLMaxTextureImageUnits;
2985 resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
2986 resources.MaxDrawBuffers = mGLMaxDrawBuffers;
2988 if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
2989 resources.EXT_frag_depth = 1;
2991 if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
2992 resources.OES_standard_derivatives = 1;
2994 if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
2995 resources.EXT_draw_buffers = 1;
2997 // Tell ANGLE to allow highp in frag shaders. (unless disabled)
2998 // If underlying GLES doesn't have highp in frag shaders, it should complain anyways.
2999 resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
3001 if (gl->WorkAroundDriverBugs()) {
3002 #ifdef XP_MACOSX
3003 if (gl->Vendor() == gl::GLVendor::NVIDIA) {
3004 // Work around bug 890432
3005 resources.MaxExpressionComplexity = 1000;
3006 }
3007 #endif
3008 }
3010 // We're storing an actual instance of StripComments because, if we don't, the
3011 // cleanSource nsAString instance will be destroyed before the reference is
3012 // actually used.
3013 StripComments stripComments(shader->Source());
3014 const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length());
3015 if (!ValidateGLSLString(cleanSource, "compileShader"))
3016 return;
3018 // shaderSource() already checks that the source stripped of comments is in the
3019 // 7-bit ASCII range, so we can skip the NS_IsAscii() check.
3020 NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
3022 if (gl->WorkAroundDriverBugs()) {
3023 const uint32_t maxSourceLength = 0x3ffff;
3024 if (sourceCString.Length() > maxSourceLength)
3025 return ErrorInvalidValue("compileShader: source has more than %d characters",
3026 maxSourceLength);
3027 }
3029 const char *s = sourceCString.get();
3031 #define WEBGL2_BYPASS_ANGLE
3032 #ifdef WEBGL2_BYPASS_ANGLE
3033 /*
3034 * The bypass don't bring a full support for GLSL ES 3.0, but the main purpose
3035 * is to natively bring gl_InstanceID (to do instanced rendering) and gl_FragData
3036 *
3037 * To remove the bypass code, just comment #define WEBGL2_BYPASS_ANGLE above
3038 *
3039 * To bypass angle, the context must be a WebGL 2 and the shader must have the
3040 * following line at the very top :
3041 * #version proto-200
3042 *
3043 * In this case, byPassANGLE == true and here is what we do :
3044 * We create two shader source code:
3045 * - one for the driver, that enable GL_EXT_gpu_shader4
3046 * - one for the angle compilor, to get informations about vertex attributes
3047 * and uniforms
3048 */
3049 static const char *bypassPrefixSearch = "#version proto-200";
3050 static const char *bypassANGLEPrefix[2] = {"precision mediump float;\n"
3051 "#define gl_VertexID 0\n"
3052 "#define gl_InstanceID 0\n",
3054 "precision mediump float;\n"
3055 "#extension GL_EXT_draw_buffers : enable\n"
3056 "#define gl_PrimitiveID 0\n"};
3058 const bool bypassANGLE = IsWebGL2() && (strstr(s, bypassPrefixSearch) != 0);
3060 const char *angleShaderCode = s;
3061 nsTArray<char> bypassANGLEShaderCode;
3062 nsTArray<char> bypassDriverShaderCode;
3064 if (bypassANGLE) {
3065 const int bypassStage = (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) ? 1 : 0;
3066 const char *originalShader = strstr(s, bypassPrefixSearch) + strlen(bypassPrefixSearch);
3067 int originalShaderSize = strlen(s) - (originalShader - s);
3068 int bypassShaderCodeSize = originalShaderSize + 4096 + 1;
3070 bypassANGLEShaderCode.SetLength(bypassShaderCodeSize);
3071 strcpy(bypassANGLEShaderCode.Elements(), bypassANGLEPrefix[bypassStage]);
3072 strcat(bypassANGLEShaderCode.Elements(), originalShader);
3074 bypassDriverShaderCode.SetLength(bypassShaderCodeSize);
3075 strcpy(bypassDriverShaderCode.Elements(), "#extension GL_EXT_gpu_shader4 : enable\n");
3076 strcat(bypassDriverShaderCode.Elements(), originalShader);
3078 angleShaderCode = bypassANGLEShaderCode.Elements();
3079 }
3080 #endif
3082 compiler = ShConstructCompiler((ShShaderType) shader->ShaderType(),
3083 SH_WEBGL_SPEC,
3084 targetShaderSourceLanguage,
3085 &resources);
3087 int compileOptions = SH_ATTRIBUTES_UNIFORMS |
3088 SH_ENFORCE_PACKING_RESTRICTIONS;
3090 if (resources.MaxExpressionComplexity > 0) {
3091 compileOptions |= SH_LIMIT_EXPRESSION_COMPLEXITY;
3092 }
3094 // We want to do this everywhere, but:
3095 #ifndef XP_MACOSX // To do this on Mac, we need to do it only on Mac OSX > 10.6 as this
3096 // causes the shader compiler in 10.6 to crash
3097 compileOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
3098 #endif
3100 if (useShaderSourceTranslation) {
3101 compileOptions |= SH_OBJECT_CODE
3102 | SH_MAP_LONG_VARIABLE_NAMES;
3104 #ifdef XP_MACOSX
3105 if (gl->WorkAroundDriverBugs()) {
3106 // Work around bug 665578 and bug 769810
3107 if (gl->Vendor() == gl::GLVendor::ATI) {
3108 compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
3109 }
3111 // Work around bug 735560
3112 if (gl->Vendor() == gl::GLVendor::Intel) {
3113 compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
3114 }
3115 }
3116 #endif
3117 }
3119 #ifdef WEBGL2_BYPASS_ANGLE
3120 if (!ShCompile(compiler, &angleShaderCode, 1, compileOptions)) {
3121 #else
3122 if (!ShCompile(compiler, &s, 1, compileOptions)) {
3123 #endif
3124 size_t lenWithNull = 0;
3125 ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &lenWithNull);
3127 if (!lenWithNull) {
3128 // Error in ShGetInfo.
3129 shader->SetTranslationFailure(NS_LITERAL_CSTRING("Internal error: failed to get shader info log"));
3130 } else {
3131 size_t len = lenWithNull - 1;
3133 nsAutoCString info;
3134 info.SetLength(len); // Allocates len+1, for the null-term.
3135 ShGetInfoLog(compiler, info.BeginWriting());
3137 shader->SetTranslationFailure(info);
3138 }
3139 ShDestruct(compiler);
3140 shader->SetCompileStatus(false);
3141 return;
3142 }
3144 size_t num_attributes = 0;
3145 ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTES, &num_attributes);
3146 size_t num_uniforms = 0;
3147 ShGetInfo(compiler, SH_ACTIVE_UNIFORMS, &num_uniforms);
3148 size_t attrib_max_length = 0;
3149 ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attrib_max_length);
3150 size_t uniform_max_length = 0;
3151 ShGetInfo(compiler, SH_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_length);
3152 size_t mapped_max_length = 0;
3153 ShGetInfo(compiler, SH_MAPPED_NAME_MAX_LENGTH, &mapped_max_length);
3155 shader->mAttribMaxNameLength = attrib_max_length;
3157 shader->mAttributes.Clear();
3158 shader->mUniforms.Clear();
3159 shader->mUniformInfos.Clear();
3161 nsAutoArrayPtr<char> attribute_name(new char[attrib_max_length+1]);
3162 nsAutoArrayPtr<char> uniform_name(new char[uniform_max_length+1]);
3163 nsAutoArrayPtr<char> mapped_name(new char[mapped_max_length+1]);
3165 for (size_t i = 0; i < num_uniforms; i++) {
3166 size_t length;
3167 int size;
3168 ShDataType type;
3169 ShGetActiveUniform(compiler, (int)i,
3170 &length, &size, &type,
3171 uniform_name,
3172 mapped_name);
3173 if (useShaderSourceTranslation) {
3174 shader->mUniforms.AppendElement(WebGLMappedIdentifier(
3175 nsDependentCString(uniform_name),
3176 nsDependentCString(mapped_name)));
3177 }
3179 // we always query uniform info, regardless of useShaderSourceTranslation,
3180 // as we need it to validate uniform setter calls, and it doesn't rely on
3181 // shader translation.
3182 char mappedNameLength = strlen(mapped_name);
3183 char mappedNameLastChar = mappedNameLength > 1
3184 ? mapped_name[mappedNameLength - 1]
3185 : 0;
3186 shader->mUniformInfos.AppendElement(WebGLUniformInfo(
3187 size,
3188 mappedNameLastChar == ']',
3189 type));
3190 }
3192 if (useShaderSourceTranslation) {
3194 for (size_t i = 0; i < num_attributes; i++) {
3195 size_t length;
3196 int size;
3197 ShDataType type;
3198 ShGetActiveAttrib(compiler, (int)i,
3199 &length, &size, &type,
3200 attribute_name,
3201 mapped_name);
3202 shader->mAttributes.AppendElement(WebGLMappedIdentifier(
3203 nsDependentCString(attribute_name),
3204 nsDependentCString(mapped_name)));
3205 }
3207 size_t lenWithNull = 0;
3208 ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &lenWithNull);
3209 MOZ_ASSERT(lenWithNull >= 1);
3210 size_t len = lenWithNull - 1;
3212 nsAutoCString translatedSrc;
3213 translatedSrc.SetLength(len); // Allocates len+1, for the null-term.
3214 ShGetObjectCode(compiler, translatedSrc.BeginWriting());
3216 CopyASCIItoUTF16(translatedSrc, shader->mTranslatedSource);
3218 const char *ts = translatedSrc.get();
3220 #ifdef WEBGL2_BYPASS_ANGLE
3221 if (bypassANGLE) {
3222 const char* driverShaderCode = bypassDriverShaderCode.Elements();
3223 gl->fShaderSource(shadername, 1, (const GLchar**) &driverShaderCode, nullptr);
3224 }
3225 else {
3226 gl->fShaderSource(shadername, 1, &ts, nullptr);
3227 }
3228 #else
3229 gl->fShaderSource(shadername, 1, &ts, nullptr);
3230 #endif
3231 } else { // not useShaderSourceTranslation
3232 // we just pass the raw untranslated shader source. We then can't use ANGLE idenfier mapping.
3233 // that's really bad, as that means we can't be 100% conformant. We should work towards always
3234 // using ANGLE identifier mapping.
3235 gl->fShaderSource(shadername, 1, &s, nullptr);
3237 CopyASCIItoUTF16(s, shader->mTranslatedSource);
3238 }
3240 shader->SetTranslationSuccess();
3242 ShDestruct(compiler);
3244 gl->fCompileShader(shadername);
3245 GLint ok;
3246 gl->fGetShaderiv(shadername, LOCAL_GL_COMPILE_STATUS, &ok);
3247 shader->SetCompileStatus(ok);
3248 }
3249 }
3251 void
3252 WebGLContext::CompressedTexImage2D(GLenum target, GLint level, GLenum internalformat,
3253 GLsizei width, GLsizei height, GLint border,
3254 const ArrayBufferView& view)
3255 {
3256 if (IsContextLost())
3257 return;
3259 const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage;
3261 if (!ValidateTexImage(2, target, level, internalformat,
3262 0, 0, 0, width, height, 0,
3263 border, internalformat, LOCAL_GL_UNSIGNED_BYTE,
3264 func))
3265 {
3266 return;
3267 }
3269 view.ComputeLengthAndData();
3271 uint32_t byteLength = view.Length();
3272 if (!ValidateCompTexImageDataSize(target, internalformat, width, height, byteLength, func)) {
3273 return;
3274 }
3276 if (!ValidateCompTexImageSize(target, level, internalformat, 0, 0,
3277 width, height, width, height, func))
3278 {
3279 return;
3280 }
3282 MakeContextCurrent();
3283 gl->fCompressedTexImage2D(target, level, internalformat, width, height, border, byteLength, view.Data());
3284 WebGLTexture* tex = activeBoundTextureForTarget(target);
3285 MOZ_ASSERT(tex);
3286 tex->SetImageInfo(target, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE,
3287 WebGLImageDataStatus::InitializedImageData);
3288 }
3290 void
3291 WebGLContext::CompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset,
3292 GLint yoffset, GLsizei width, GLsizei height,
3293 GLenum format, const ArrayBufferView& view)
3294 {
3295 if (IsContextLost())
3296 return;
3298 const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage;
3300 if (!ValidateTexImage(2, target,
3301 level, format,
3302 xoffset, yoffset, 0,
3303 width, height, 0,
3304 0, format, LOCAL_GL_UNSIGNED_BYTE,
3305 func))
3306 {
3307 return;
3308 }
3310 WebGLTexture *tex = activeBoundTextureForTarget(target);
3311 MOZ_ASSERT(tex);
3312 WebGLTexture::ImageInfo& levelInfo = tex->ImageInfoAt(target, level);
3314 view.ComputeLengthAndData();
3316 uint32_t byteLength = view.Length();
3317 if (!ValidateCompTexImageDataSize(target, format, width, height, byteLength, func))
3318 return;
3320 if (!ValidateCompTexImageSize(target, level, format,
3321 xoffset, yoffset,
3322 width, height,
3323 levelInfo.Width(), levelInfo.Height(),
3324 func))
3325 {
3326 return;
3327 }
3329 if (levelInfo.HasUninitializedImageData())
3330 tex->DoDeferredImageInitialization(target, level);
3332 MakeContextCurrent();
3333 gl->fCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, byteLength, view.Data());
3334 }
3336 JS::Value
3337 WebGLContext::GetShaderParameter(WebGLShader *shader, GLenum pname)
3338 {
3339 if (IsContextLost())
3340 return JS::NullValue();
3342 if (!ValidateObject("getShaderParameter: shader", shader))
3343 return JS::NullValue();
3345 GLuint shadername = shader->GLName();
3347 MakeContextCurrent();
3349 switch (pname) {
3350 case LOCAL_GL_SHADER_TYPE:
3351 {
3352 GLint i = 0;
3353 gl->fGetShaderiv(shadername, pname, &i);
3354 return JS::NumberValue(uint32_t(i));
3355 }
3356 break;
3357 case LOCAL_GL_DELETE_STATUS:
3358 return JS::BooleanValue(shader->IsDeleteRequested());
3359 break;
3360 case LOCAL_GL_COMPILE_STATUS:
3361 {
3362 GLint i = 0;
3363 gl->fGetShaderiv(shadername, pname, &i);
3364 return JS::BooleanValue(bool(i));
3365 }
3366 break;
3367 default:
3368 ErrorInvalidEnumInfo("getShaderParameter: parameter", pname);
3369 }
3371 return JS::NullValue();
3372 }
3374 void
3375 WebGLContext::GetShaderInfoLog(WebGLShader *shader, nsAString& retval)
3376 {
3377 nsAutoCString s;
3378 GetShaderInfoLog(shader, s);
3379 if (s.IsVoid())
3380 retval.SetIsVoid(true);
3381 else
3382 CopyASCIItoUTF16(s, retval);
3383 }
3385 void
3386 WebGLContext::GetShaderInfoLog(WebGLShader *shader, nsACString& retval)
3387 {
3388 if (IsContextLost())
3389 {
3390 retval.SetIsVoid(true);
3391 return;
3392 }
3394 if (!ValidateObject("getShaderInfoLog: shader", shader))
3395 return;
3397 retval = shader->TranslationLog();
3398 if (!retval.IsVoid()) {
3399 return;
3400 }
3402 MakeContextCurrent();
3404 GLuint shadername = shader->GLName();
3405 GLint k = -1;
3406 gl->fGetShaderiv(shadername, LOCAL_GL_INFO_LOG_LENGTH, &k);
3407 if (k == -1) {
3408 // XXX GL Error? should never happen.
3409 return;
3410 }
3412 if (k == 0) {
3413 retval.Truncate();
3414 return;
3415 }
3417 retval.SetCapacity(k);
3418 gl->fGetShaderInfoLog(shadername, k, &k, (char*) retval.BeginWriting());
3419 retval.SetLength(k);
3420 }
3422 already_AddRefed<WebGLShaderPrecisionFormat>
3423 WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
3424 {
3425 if (IsContextLost())
3426 return nullptr;
3428 switch (shadertype) {
3429 case LOCAL_GL_FRAGMENT_SHADER:
3430 case LOCAL_GL_VERTEX_SHADER:
3431 break;
3432 default:
3433 ErrorInvalidEnumInfo("getShaderPrecisionFormat: shadertype", shadertype);
3434 return nullptr;
3435 }
3437 switch (precisiontype) {
3438 case LOCAL_GL_LOW_FLOAT:
3439 case LOCAL_GL_MEDIUM_FLOAT:
3440 case LOCAL_GL_HIGH_FLOAT:
3441 case LOCAL_GL_LOW_INT:
3442 case LOCAL_GL_MEDIUM_INT:
3443 case LOCAL_GL_HIGH_INT:
3444 break;
3445 default:
3446 ErrorInvalidEnumInfo("getShaderPrecisionFormat: precisiontype", precisiontype);
3447 return nullptr;
3448 }
3450 MakeContextCurrent();
3451 GLint range[2], precision;
3453 if (mDisableFragHighP &&
3454 shadertype == LOCAL_GL_FRAGMENT_SHADER &&
3455 (precisiontype == LOCAL_GL_HIGH_FLOAT ||
3456 precisiontype == LOCAL_GL_HIGH_INT))
3457 {
3458 precision = 0;
3459 range[0] = 0;
3460 range[1] = 0;
3461 } else {
3462 gl->fGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision);
3463 }
3465 nsRefPtr<WebGLShaderPrecisionFormat> retShaderPrecisionFormat
3466 = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision);
3467 return retShaderPrecisionFormat.forget();
3468 }
3470 void
3471 WebGLContext::GetShaderSource(WebGLShader *shader, nsAString& retval)
3472 {
3473 if (IsContextLost()) {
3474 retval.SetIsVoid(true);
3475 return;
3476 }
3478 if (!ValidateObject("getShaderSource: shader", shader))
3479 return;
3481 retval.Assign(shader->Source());
3482 }
3484 void
3485 WebGLContext::ShaderSource(WebGLShader *shader, const nsAString& source)
3486 {
3487 if (IsContextLost())
3488 return;
3490 if (!ValidateObject("shaderSource: shader", shader))
3491 return;
3493 // We're storing an actual instance of StripComments because, if we don't, the
3494 // cleanSource nsAString instance will be destroyed before the reference is
3495 // actually used.
3496 StripComments stripComments(source);
3497 const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length());
3498 if (!ValidateGLSLString(cleanSource, "compileShader"))
3499 return;
3501 shader->SetSource(source);
3503 shader->SetNeedsTranslation();
3504 }
3506 void
3507 WebGLContext::GetShaderTranslatedSource(WebGLShader *shader, nsAString& retval)
3508 {
3509 if (IsContextLost()) {
3510 retval.SetIsVoid(true);
3511 return;
3512 }
3514 if (!ValidateObject("getShaderTranslatedSource: shader", shader))
3515 return;
3517 retval.Assign(shader->TranslatedSource());
3518 }
3520 GLenum WebGLContext::CheckedTexImage2D(GLenum target,
3521 GLint level,
3522 GLenum internalFormat,
3523 GLsizei width,
3524 GLsizei height,
3525 GLint border,
3526 GLenum format,
3527 GLenum type,
3528 const GLvoid *data)
3529 {
3530 MOZ_ASSERT(internalFormat == format);
3531 WebGLTexture *tex = activeBoundTextureForTarget(target);
3532 MOZ_ASSERT(tex != nullptr, "no texture bound");
3534 bool sizeMayChange = true;
3536 if (tex->HasImageInfoAt(target, level)) {
3537 const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level);
3538 sizeMayChange = width != imageInfo.Width() ||
3539 height != imageInfo.Height() ||
3540 format != imageInfo.WebGLFormat() ||
3541 type != imageInfo.WebGLType();
3542 }
3544 // Convert to format and type required by OpenGL 'driver'.
3545 GLenum driverType = DriverTypeFromType(gl, type);
3546 GLenum driverInternalFormat = LOCAL_GL_NONE;
3547 GLenum driverFormat = LOCAL_GL_NONE;
3548 DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
3550 if (sizeMayChange) {
3551 GetAndFlushUnderlyingGLErrors();
3552 }
3554 gl->fTexImage2D(target, level, driverInternalFormat, width, height, border, driverFormat, driverType, data);
3556 GLenum error = LOCAL_GL_NO_ERROR;
3557 if (sizeMayChange) {
3558 error = GetAndFlushUnderlyingGLErrors();
3559 }
3561 return error;
3562 }
3564 void
3565 WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat,
3566 GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
3567 GLint border,
3568 GLenum format, GLenum type,
3569 void* data, uint32_t byteLength,
3570 int jsArrayType, // a TypedArray format enum, or -1 if not relevant
3571 WebGLTexelFormat srcFormat, bool srcPremultiplied)
3572 {
3573 const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
3575 if (!ValidateTexImage(2, target, level, internalformat,
3576 0, 0, 0,
3577 width, height, 0,
3578 border, format, type, func))
3579 {
3580 return;
3581 }
3583 const bool isDepthTexture = format == LOCAL_GL_DEPTH_COMPONENT ||
3584 format == LOCAL_GL_DEPTH_STENCIL;
3586 if (isDepthTexture) {
3587 if (data != nullptr || level != 0)
3588 return ErrorInvalidOperation("texImage2D: "
3589 "with format of DEPTH_COMPONENT or DEPTH_STENCIL, "
3590 "data must be nullptr, "
3591 "level must be zero");
3592 }
3594 if (!ValidateTexInputData(type, jsArrayType, func))
3595 return;
3597 WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
3598 WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
3600 uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
3602 CheckedUint32 checked_neededByteLength =
3603 GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
3605 CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
3606 CheckedUint32 checked_alignedRowSize =
3607 RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
3609 if (!checked_neededByteLength.isValid())
3610 return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
3612 uint32_t bytesNeeded = checked_neededByteLength.value();
3614 if (byteLength && byteLength < bytesNeeded)
3615 return ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)",
3616 bytesNeeded, byteLength);
3618 WebGLTexture *tex = activeBoundTextureForTarget(target);
3620 if (!tex)
3621 return ErrorInvalidOperation("texImage2D: no texture is bound to this target");
3623 MakeContextCurrent();
3625 nsAutoArrayPtr<uint8_t> convertedData;
3626 void* pixels = nullptr;
3627 WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
3629 if (byteLength) {
3630 size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
3631 uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
3632 size_t dstPlainRowSize = dstTexelSize * width;
3633 size_t unpackAlignment = mPixelStoreUnpackAlignment;
3634 size_t dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment;
3636 if (actualSrcFormat == dstFormat &&
3637 srcPremultiplied == mPixelStorePremultiplyAlpha &&
3638 srcStride == dstStride &&
3639 !mPixelStoreFlipY)
3640 {
3641 // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
3642 pixels = data;
3643 }
3644 else
3645 {
3646 size_t convertedDataSize = height * dstStride;
3647 convertedData = new uint8_t[convertedDataSize];
3648 ConvertImage(width, height, srcStride, dstStride,
3649 static_cast<uint8_t*>(data), convertedData,
3650 actualSrcFormat, srcPremultiplied,
3651 dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
3652 pixels = reinterpret_cast<void*>(convertedData.get());
3653 }
3654 imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData;
3655 }
3657 GLenum error = CheckedTexImage2D(target, level, internalformat, width,
3658 height, border, format, type, pixels);
3660 if (error) {
3661 GenerateWarning("texImage2D generated error %s", ErrorName(error));
3662 return;
3663 }
3665 // in all of the code paths above, we should have either initialized data,
3666 // or allocated data and left it uninitialized, but in any case we shouldn't
3667 // have NoImageData at this point.
3668 MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData);
3670 tex->SetImageInfo(target, level, width, height, format, type, imageInfoStatusIfSuccess);
3671 }
3673 void
3674 WebGLContext::TexImage2D(GLenum target, GLint level,
3675 GLenum internalformat, GLsizei width,
3676 GLsizei height, GLint border, GLenum format,
3677 GLenum type, const Nullable<ArrayBufferView> &pixels, ErrorResult& rv)
3678 {
3679 if (IsContextLost())
3680 return;
3682 void* data;
3683 uint32_t length;
3684 int jsArrayType;
3685 if (pixels.IsNull()) {
3686 data = nullptr;
3687 length = 0;
3688 jsArrayType = -1;
3689 } else {
3690 const ArrayBufferView& view = pixels.Value();
3691 view.ComputeLengthAndData();
3693 data = view.Data();
3694 length = view.Length();
3695 jsArrayType = int(JS_GetArrayBufferViewType(view.Obj()));
3696 }
3698 return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type,
3699 data, length, jsArrayType,
3700 WebGLTexelFormat::Auto, false);
3701 }
3703 void
3704 WebGLContext::TexImage2D(GLenum target, GLint level,
3705 GLenum internalformat, GLenum format,
3706 GLenum type, ImageData* pixels, ErrorResult& rv)
3707 {
3708 if (IsContextLost())
3709 return;
3711 if (!pixels) {
3712 // Spec says to generate an INVALID_VALUE error
3713 return ErrorInvalidValue("texImage2D: null ImageData");
3714 }
3716 Uint8ClampedArray arr(pixels->GetDataObject());
3717 arr.ComputeLengthAndData();
3719 return TexImage2D_base(target, level, internalformat, pixels->Width(),
3720 pixels->Height(), 4*pixels->Width(), 0,
3721 format, type, arr.Data(), arr.Length(), -1,
3722 WebGLTexelFormat::RGBA8, false);
3723 }
3726 void
3727 WebGLContext::TexSubImage2D_base(GLenum target, GLint level,
3728 GLint xoffset, GLint yoffset,
3729 GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
3730 GLenum format, GLenum type,
3731 void* data, uint32_t byteLength,
3732 int jsArrayType,
3733 WebGLTexelFormat srcFormat, bool srcPremultiplied)
3734 {
3735 const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
3737 if (!ValidateTexImage(2, target, level, format,
3738 xoffset, yoffset, 0,
3739 width, height, 0,
3740 0, format, type, func))
3741 {
3742 return;
3743 }
3745 if (!ValidateTexInputData(type, jsArrayType, func))
3746 return;
3748 WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type);
3749 WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat;
3751 uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat);
3753 if (width == 0 || height == 0)
3754 return; // ES 2.0 says it has no effect, we better return right now
3756 CheckedUint32 checked_neededByteLength =
3757 GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
3759 CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
3761 CheckedUint32 checked_alignedRowSize =
3762 RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
3764 if (!checked_neededByteLength.isValid())
3765 return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
3767 uint32_t bytesNeeded = checked_neededByteLength.value();
3769 if (byteLength < bytesNeeded)
3770 return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
3772 WebGLTexture *tex = activeBoundTextureForTarget(target);
3773 const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level);
3775 if (imageInfo.HasUninitializedImageData())
3776 tex->DoDeferredImageInitialization(target, level);
3778 MakeContextCurrent();
3780 size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
3781 uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8;
3782 size_t dstPlainRowSize = dstTexelSize * width;
3783 // There are checks above to ensure that this won't overflow.
3784 size_t dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value();
3786 void* pixels = data;
3787 nsAutoArrayPtr<uint8_t> convertedData;
3789 // no conversion, no flipping, so we avoid copying anything and just pass the source pointer
3790 bool noConversion = (actualSrcFormat == dstFormat &&
3791 srcPremultiplied == mPixelStorePremultiplyAlpha &&
3792 srcStride == dstStride &&
3793 !mPixelStoreFlipY);
3795 if (!noConversion) {
3796 size_t convertedDataSize = height * dstStride;
3797 convertedData = new uint8_t[convertedDataSize];
3798 ConvertImage(width, height, srcStride, dstStride,
3799 static_cast<const uint8_t*>(data), convertedData,
3800 actualSrcFormat, srcPremultiplied,
3801 dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize);
3802 pixels = reinterpret_cast<void*>(convertedData.get());
3803 }
3805 GLenum driverType = DriverTypeFromType(gl, type);
3806 GLenum driverInternalFormat = LOCAL_GL_NONE;
3807 GLenum driverFormat = LOCAL_GL_NONE;
3808 DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
3810 gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, driverFormat, driverType, pixels);
3811 }
3813 void
3814 WebGLContext::TexSubImage2D(GLenum target, GLint level,
3815 GLint xoffset, GLint yoffset,
3816 GLsizei width, GLsizei height,
3817 GLenum format, GLenum type,
3818 const Nullable<ArrayBufferView> &pixels,
3819 ErrorResult& rv)
3820 {
3821 if (IsContextLost())
3822 return;
3824 if (pixels.IsNull())
3825 return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
3827 const ArrayBufferView& view = pixels.Value();
3828 view.ComputeLengthAndData();
3830 return TexSubImage2D_base(target, level, xoffset, yoffset,
3831 width, height, 0, format, type,
3832 view.Data(), view.Length(),
3833 JS_GetArrayBufferViewType(view.Obj()),
3834 WebGLTexelFormat::Auto, false);
3835 }
3837 void
3838 WebGLContext::TexSubImage2D(GLenum target, GLint level,
3839 GLint xoffset, GLint yoffset,
3840 GLenum format, GLenum type, ImageData* pixels,
3841 ErrorResult& rv)
3842 {
3843 if (IsContextLost())
3844 return;
3846 if (!pixels)
3847 return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
3849 Uint8ClampedArray arr(pixels->GetDataObject());
3850 arr.ComputeLengthAndData();
3852 return TexSubImage2D_base(target, level, xoffset, yoffset,
3853 pixels->Width(), pixels->Height(),
3854 4*pixels->Width(), format, type,
3855 arr.Data(), arr.Length(),
3856 -1,
3857 WebGLTexelFormat::RGBA8, false);
3858 }
3860 bool
3861 WebGLContext::LoseContext()
3862 {
3863 if (IsContextLost())
3864 return false;
3866 ForceLoseContext();
3868 return true;
3869 }
3871 bool
3872 WebGLContext::RestoreContext()
3873 {
3874 if (!IsContextLost() || !mAllowRestore) {
3875 return false;
3876 }
3878 ForceRestoreContext();
3880 return true;
3881 }
3883 bool
3884 BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize)
3885 {
3886 switch (uType) {
3887 case LOCAL_GL_INT:
3888 case LOCAL_GL_INT_VEC2:
3889 case LOCAL_GL_INT_VEC3:
3890 case LOCAL_GL_INT_VEC4:
3891 case LOCAL_GL_SAMPLER_2D:
3892 case LOCAL_GL_SAMPLER_CUBE:
3893 *baseType = LOCAL_GL_INT;
3894 break;
3895 case LOCAL_GL_FLOAT:
3896 case LOCAL_GL_FLOAT_VEC2:
3897 case LOCAL_GL_FLOAT_VEC3:
3898 case LOCAL_GL_FLOAT_VEC4:
3899 case LOCAL_GL_FLOAT_MAT2:
3900 case LOCAL_GL_FLOAT_MAT3:
3901 case LOCAL_GL_FLOAT_MAT4:
3902 *baseType = LOCAL_GL_FLOAT;
3903 break;
3904 case LOCAL_GL_BOOL:
3905 case LOCAL_GL_BOOL_VEC2:
3906 case LOCAL_GL_BOOL_VEC3:
3907 case LOCAL_GL_BOOL_VEC4:
3908 *baseType = LOCAL_GL_BOOL; // pretend these are int
3909 break;
3910 default:
3911 return false;
3912 }
3914 switch (uType) {
3915 case LOCAL_GL_INT:
3916 case LOCAL_GL_FLOAT:
3917 case LOCAL_GL_BOOL:
3918 case LOCAL_GL_SAMPLER_2D:
3919 case LOCAL_GL_SAMPLER_CUBE:
3920 *unitSize = 1;
3921 break;
3922 case LOCAL_GL_INT_VEC2:
3923 case LOCAL_GL_FLOAT_VEC2:
3924 case LOCAL_GL_BOOL_VEC2:
3925 *unitSize = 2;
3926 break;
3927 case LOCAL_GL_INT_VEC3:
3928 case LOCAL_GL_FLOAT_VEC3:
3929 case LOCAL_GL_BOOL_VEC3:
3930 *unitSize = 3;
3931 break;
3932 case LOCAL_GL_INT_VEC4:
3933 case LOCAL_GL_FLOAT_VEC4:
3934 case LOCAL_GL_BOOL_VEC4:
3935 *unitSize = 4;
3936 break;
3937 case LOCAL_GL_FLOAT_MAT2:
3938 *unitSize = 4;
3939 break;
3940 case LOCAL_GL_FLOAT_MAT3:
3941 *unitSize = 9;
3942 break;
3943 case LOCAL_GL_FLOAT_MAT4:
3944 *unitSize = 16;
3945 break;
3946 default:
3947 return false;
3948 }
3950 return true;
3951 }
3954 WebGLTexelFormat mozilla::GetWebGLTexelFormat(GLenum internalformat, GLenum type)
3955 {
3956 //
3957 // WEBGL_depth_texture
3958 if (internalformat == LOCAL_GL_DEPTH_COMPONENT) {
3959 switch (type) {
3960 case LOCAL_GL_UNSIGNED_SHORT:
3961 return WebGLTexelFormat::D16;
3962 case LOCAL_GL_UNSIGNED_INT:
3963 return WebGLTexelFormat::D32;
3964 }
3966 MOZ_CRASH("Invalid WebGL texture format/type?");
3967 }
3969 if (internalformat == LOCAL_GL_DEPTH_STENCIL) {
3970 switch (type) {
3971 case LOCAL_GL_UNSIGNED_INT_24_8_EXT:
3972 return WebGLTexelFormat::D24S8;
3973 }
3975 MOZ_CRASH("Invalid WebGL texture format/type?");
3976 }
3978 if (internalformat == LOCAL_GL_DEPTH_COMPONENT16) {
3979 return WebGLTexelFormat::D16;
3980 }
3982 if (internalformat == LOCAL_GL_DEPTH_COMPONENT32) {
3983 return WebGLTexelFormat::D32;
3984 }
3986 if (internalformat == LOCAL_GL_DEPTH24_STENCIL8) {
3987 return WebGLTexelFormat::D24S8;
3988 }
3990 if (type == LOCAL_GL_UNSIGNED_BYTE) {
3991 switch (internalformat) {
3992 case LOCAL_GL_RGBA:
3993 case LOCAL_GL_SRGB_ALPHA_EXT:
3994 return WebGLTexelFormat::RGBA8;
3995 case LOCAL_GL_RGB:
3996 case LOCAL_GL_SRGB_EXT:
3997 return WebGLTexelFormat::RGB8;
3998 case LOCAL_GL_ALPHA:
3999 return WebGLTexelFormat::A8;
4000 case LOCAL_GL_LUMINANCE:
4001 return WebGLTexelFormat::R8;
4002 case LOCAL_GL_LUMINANCE_ALPHA:
4003 return WebGLTexelFormat::RA8;
4004 }
4006 MOZ_CRASH("Invalid WebGL texture format/type?");
4007 }
4009 if (type == LOCAL_GL_FLOAT) {
4010 // OES_texture_float
4011 switch (internalformat) {
4012 case LOCAL_GL_RGBA:
4013 case LOCAL_GL_RGBA32F:
4014 return WebGLTexelFormat::RGBA32F;
4015 case LOCAL_GL_RGB:
4016 case LOCAL_GL_RGB32F:
4017 return WebGLTexelFormat::RGB32F;
4018 case LOCAL_GL_ALPHA:
4019 case LOCAL_GL_ALPHA32F_ARB:
4020 return WebGLTexelFormat::A32F;
4021 case LOCAL_GL_LUMINANCE:
4022 case LOCAL_GL_LUMINANCE32F_ARB:
4023 return WebGLTexelFormat::R32F;
4024 case LOCAL_GL_LUMINANCE_ALPHA:
4025 case LOCAL_GL_LUMINANCE_ALPHA32F_ARB:
4026 return WebGLTexelFormat::RA32F;
4027 }
4029 MOZ_CRASH("Invalid WebGL texture format/type?");
4030 } else if (type == LOCAL_GL_HALF_FLOAT_OES) {
4031 // OES_texture_half_float
4032 switch (internalformat) {
4033 case LOCAL_GL_RGBA:
4034 case LOCAL_GL_RGBA16F:
4035 return WebGLTexelFormat::RGBA16F;
4036 case LOCAL_GL_RGB:
4037 case LOCAL_GL_RGB16F:
4038 return WebGLTexelFormat::RGB16F;
4039 case LOCAL_GL_ALPHA:
4040 case LOCAL_GL_ALPHA16F_ARB:
4041 return WebGLTexelFormat::A16F;
4042 case LOCAL_GL_LUMINANCE:
4043 case LOCAL_GL_LUMINANCE16F_ARB:
4044 return WebGLTexelFormat::R16F;
4045 case LOCAL_GL_LUMINANCE_ALPHA:
4046 case LOCAL_GL_LUMINANCE_ALPHA16F_ARB:
4047 return WebGLTexelFormat::RA16F;
4048 default:
4049 MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
4050 return WebGLTexelFormat::BadFormat;
4051 }
4052 }
4054 switch (type) {
4055 case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
4056 return WebGLTexelFormat::RGBA4444;
4057 case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
4058 return WebGLTexelFormat::RGBA5551;
4059 case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
4060 return WebGLTexelFormat::RGB565;
4061 default:
4062 MOZ_ASSERT(false, "Coding mistake?! Should never reach this point.");
4063 return WebGLTexelFormat::BadFormat;
4064 }
4066 MOZ_CRASH("Invalid WebGL texture format/type?");
4067 }
4069 void
4070 WebGLContext::BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) {
4071 if (IsContextLost())
4072 return;
4073 MakeContextCurrent();
4074 gl->fBlendColor(r, g, b, a);
4075 }
4077 void
4078 WebGLContext::Flush() {
4079 if (IsContextLost())
4080 return;
4081 MakeContextCurrent();
4082 gl->fFlush();
4083 }
4085 void
4086 WebGLContext::Finish() {
4087 if (IsContextLost())
4088 return;
4089 MakeContextCurrent();
4090 gl->fFinish();
4091 }
4093 void
4094 WebGLContext::LineWidth(GLfloat width) {
4095 if (IsContextLost())
4096 return;
4097 MakeContextCurrent();
4098 gl->fLineWidth(width);
4099 }
4101 void
4102 WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) {
4103 if (IsContextLost())
4104 return;
4105 MakeContextCurrent();
4106 gl->fPolygonOffset(factor, units);
4107 }
4109 void
4110 WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) {
4111 if (IsContextLost())
4112 return;
4113 MakeContextCurrent();
4114 gl->fSampleCoverage(value, invert);
4115 }