content/canvas/src/WebGLContextGL.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial