michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WebGLContext.h" michael@0: #include "WebGLContextUtils.h" michael@0: #include "WebGLBuffer.h" michael@0: #include "WebGLVertexAttribData.h" michael@0: #include "WebGLShader.h" michael@0: #include "WebGLProgram.h" michael@0: #include "WebGLUniformLocation.h" michael@0: #include "WebGLFramebuffer.h" michael@0: #include "WebGLRenderbuffer.h" michael@0: #include "WebGLShaderPrecisionFormat.h" michael@0: #include "WebGLTexture.h" michael@0: #include "WebGLExtensions.h" michael@0: #include "WebGLVertexArray.h" michael@0: michael@0: #include "nsString.h" michael@0: #include "nsDebug.h" michael@0: michael@0: #include "gfxContext.h" michael@0: #include "gfxPlatform.h" michael@0: #include "GLContext.h" michael@0: michael@0: #include "nsContentUtils.h" michael@0: #include "nsError.h" michael@0: #include "nsLayoutUtils.h" michael@0: michael@0: #include "CanvasUtils.h" michael@0: #include "gfxUtils.h" michael@0: michael@0: #include "jsfriendapi.h" michael@0: michael@0: #include "WebGLTexelConversions.h" michael@0: #include "WebGLValidateStrings.h" michael@0: #include michael@0: michael@0: // needed to check if current OS is lower than 10.7 michael@0: #if defined(MOZ_WIDGET_COCOA) michael@0: #include "nsCocoaFeatures.h" michael@0: #endif michael@0: michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/dom/ImageData.h" michael@0: #include "mozilla/dom/ToJSValue.h" michael@0: #include "mozilla/Endian.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::gl; michael@0: using namespace mozilla::gfx; michael@0: michael@0: static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize); michael@0: michael@0: const WebGLRectangleObject* michael@0: WebGLContext::CurValidFBRectObject() const michael@0: { michael@0: const WebGLRectangleObject* rect = nullptr; michael@0: michael@0: if (mBoundFramebuffer) { michael@0: // We don't really need to ask the driver. michael@0: // Use 'precheck' to just check that our internal state looks good. michael@0: GLenum precheckStatus = mBoundFramebuffer->PrecheckFramebufferStatus(); michael@0: if (precheckStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE) michael@0: rect = &mBoundFramebuffer->RectangleObject(); michael@0: } else { michael@0: rect = static_cast(this); michael@0: } michael@0: michael@0: return rect; michael@0: } michael@0: michael@0: // michael@0: // WebGL API michael@0: // michael@0: michael@0: void michael@0: WebGLContext::ActiveTexture(GLenum texture) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (texture < LOCAL_GL_TEXTURE0 || michael@0: texture >= LOCAL_GL_TEXTURE0 + uint32_t(mGLMaxTextureUnits)) michael@0: { michael@0: return ErrorInvalidEnum( michael@0: "ActiveTexture: texture unit %d out of range. " michael@0: "Accepted values range from TEXTURE0 to TEXTURE0 + %d. " michael@0: "Notice that TEXTURE0 != 0.", michael@0: texture, mGLMaxTextureUnits); michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: mActiveTexture = texture - LOCAL_GL_TEXTURE0; michael@0: gl->fActiveTexture(texture); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::AttachShader(WebGLProgram *program, WebGLShader *shader) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObject("attachShader: program", program) || michael@0: !ValidateObject("attachShader: shader", shader)) michael@0: return; michael@0: michael@0: // Per GLSL ES 2.0, we can only have one of each type of shader michael@0: // attached. This renders the next test somewhat moot, but we'll michael@0: // leave it for when we support more than one shader of each type. michael@0: if (program->HasAttachedShaderOfType(shader->ShaderType())) michael@0: return ErrorInvalidOperation("attachShader: only one of each type of shader may be attached to a program"); michael@0: michael@0: if (!program->AttachShader(shader)) michael@0: return ErrorInvalidOperation("attachShader: shader is already attached"); michael@0: } michael@0: michael@0: michael@0: void michael@0: WebGLContext::BindAttribLocation(WebGLProgram *prog, GLuint location, michael@0: const nsAString& name) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObject("bindAttribLocation: program", prog)) michael@0: return; michael@0: michael@0: GLuint progname = prog->GLName(); michael@0: michael@0: if (!ValidateGLSLVariableName(name, "bindAttribLocation")) michael@0: return; michael@0: michael@0: if (!ValidateAttribIndex(location, "bindAttribLocation")) michael@0: return; michael@0: michael@0: NS_LossyConvertUTF16toASCII cname(name); michael@0: nsCString mappedName; michael@0: prog->MapIdentifier(cname, &mappedName); michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fBindAttribLocation(progname, location, mappedName.get()); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer *wfb) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (target != LOCAL_GL_FRAMEBUFFER) michael@0: return ErrorInvalidEnum("bindFramebuffer: target must be GL_FRAMEBUFFER"); michael@0: michael@0: if (!ValidateObjectAllowDeletedOrNull("bindFramebuffer", wfb)) michael@0: return; michael@0: michael@0: // silently ignore a deleted frame buffer michael@0: if (wfb && wfb->IsDeleted()) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: if (!wfb) { michael@0: gl->fBindFramebuffer(target, 0); michael@0: } else { michael@0: GLuint framebuffername = wfb->GLName(); michael@0: gl->fBindFramebuffer(target, framebuffername); michael@0: wfb->SetHasEverBeenBound(true); michael@0: } michael@0: michael@0: mBoundFramebuffer = wfb; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer *wrb) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (target != LOCAL_GL_RENDERBUFFER) michael@0: return ErrorInvalidEnumInfo("bindRenderbuffer: target", target); michael@0: michael@0: if (!ValidateObjectAllowDeletedOrNull("bindRenderbuffer", wrb)) michael@0: return; michael@0: michael@0: // silently ignore a deleted buffer michael@0: if (wrb && wrb->IsDeleted()) michael@0: return; michael@0: michael@0: if (wrb) michael@0: wrb->SetHasEverBeenBound(true); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: // Sometimes we emulate renderbuffers (depth-stencil emu), so there's not michael@0: // always a 1-1 mapping from `wrb` to GL name. Just have `wrb` handle it. michael@0: if (wrb) { michael@0: wrb->BindRenderbuffer(); michael@0: } else { michael@0: gl->fBindRenderbuffer(target, 0); michael@0: } michael@0: michael@0: mBoundRenderbuffer = wrb; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::BindTexture(GLenum target, WebGLTexture *newTex) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex)) michael@0: return; michael@0: michael@0: // silently ignore a deleted texture michael@0: if (newTex && newTex->IsDeleted()) michael@0: return; michael@0: michael@0: WebGLRefPtr* currentTexPtr = nullptr; michael@0: michael@0: if (target == LOCAL_GL_TEXTURE_2D) { michael@0: currentTexPtr = &mBound2DTextures[mActiveTexture]; michael@0: } else if (target == LOCAL_GL_TEXTURE_CUBE_MAP) { michael@0: currentTexPtr = &mBoundCubeMapTextures[mActiveTexture]; michael@0: } else { michael@0: return ErrorInvalidEnumInfo("bindTexture: target", target); michael@0: } michael@0: michael@0: WebGLTextureFakeBlackStatus currentTexFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; michael@0: if (*currentTexPtr) { michael@0: currentTexFakeBlackStatus = (*currentTexPtr)->ResolvedFakeBlackStatus(); michael@0: } michael@0: WebGLTextureFakeBlackStatus newTexFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; michael@0: if (newTex) { michael@0: newTexFakeBlackStatus = newTex->ResolvedFakeBlackStatus(); michael@0: } michael@0: michael@0: *currentTexPtr = newTex; michael@0: michael@0: if (currentTexFakeBlackStatus != newTexFakeBlackStatus) { michael@0: SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown); michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: if (newTex) michael@0: newTex->Bind(target); michael@0: else michael@0: gl->fBindTexture(target, 0 /* == texturename */); michael@0: } michael@0: michael@0: void WebGLContext::BlendEquation(GLenum mode) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateBlendEquationEnum(mode, "blendEquation: mode")) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fBlendEquation(mode); michael@0: } michael@0: michael@0: void WebGLContext::BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateBlendEquationEnum(modeRGB, "blendEquationSeparate: modeRGB") || michael@0: !ValidateBlendEquationEnum(modeAlpha, "blendEquationSeparate: modeAlpha")) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fBlendEquationSeparate(modeRGB, modeAlpha); michael@0: } michael@0: michael@0: void WebGLContext::BlendFunc(GLenum sfactor, GLenum dfactor) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateBlendFuncSrcEnum(sfactor, "blendFunc: sfactor") || michael@0: !ValidateBlendFuncDstEnum(dfactor, "blendFunc: dfactor")) michael@0: return; michael@0: michael@0: if (!ValidateBlendFuncEnumsCompatibility(sfactor, dfactor, "blendFuncSeparate: srcRGB and dstRGB")) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fBlendFunc(sfactor, dfactor); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, michael@0: GLenum srcAlpha, GLenum dstAlpha) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateBlendFuncSrcEnum(srcRGB, "blendFuncSeparate: srcRGB") || michael@0: !ValidateBlendFuncSrcEnum(srcAlpha, "blendFuncSeparate: srcAlpha") || michael@0: !ValidateBlendFuncDstEnum(dstRGB, "blendFuncSeparate: dstRGB") || michael@0: !ValidateBlendFuncDstEnum(dstAlpha, "blendFuncSeparate: dstAlpha")) michael@0: return; michael@0: michael@0: // note that we only check compatibity for the RGB enums, no need to for the Alpha enums, see michael@0: // "Section 6.8 forgetting to mention alpha factors?" thread on the public_webgl mailing list michael@0: if (!ValidateBlendFuncEnumsCompatibility(srcRGB, dstRGB, "blendFuncSeparate: srcRGB and dstRGB")) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); michael@0: } michael@0: michael@0: GLenum michael@0: WebGLContext::CheckFramebufferStatus(GLenum target) michael@0: { michael@0: if (IsContextLost()) michael@0: return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; michael@0: michael@0: if (target != LOCAL_GL_FRAMEBUFFER) { michael@0: ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER"); michael@0: return 0; michael@0: } michael@0: michael@0: if (!mBoundFramebuffer) michael@0: return LOCAL_GL_FRAMEBUFFER_COMPLETE; michael@0: michael@0: return mBoundFramebuffer->CheckFramebufferStatus(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::CopyTexSubImage2D_base(GLenum target, michael@0: GLint level, michael@0: GLenum internalformat, michael@0: GLint xoffset, michael@0: GLint yoffset, michael@0: GLint x, michael@0: GLint y, michael@0: GLsizei width, michael@0: GLsizei height, michael@0: bool sub) michael@0: { michael@0: const WebGLRectangleObject* framebufferRect = CurValidFBRectObject(); michael@0: GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0; michael@0: GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0; michael@0: michael@0: const char* info = sub ? "copyTexSubImage2D" : "copyTexImage2D"; michael@0: WebGLTexImageFunc func = sub ? WebGLTexImageFunc::CopyTexSubImage : WebGLTexImageFunc::CopyTexImage; michael@0: michael@0: // TODO: This changes with color_buffer_float. Reassess when the michael@0: // patch lands. michael@0: if (!ValidateTexImage(2, target, level, internalformat, michael@0: xoffset, yoffset, 0, michael@0: width, height, 0, michael@0: 0, internalformat, LOCAL_GL_UNSIGNED_BYTE, michael@0: func)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: WebGLTexture *tex = activeBoundTextureForTarget(target); michael@0: michael@0: if (!tex) michael@0: return ErrorInvalidOperation("%s: no texture is bound to this target"); michael@0: michael@0: if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) { michael@0: if (sub) michael@0: gl->fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); michael@0: else michael@0: gl->fCopyTexImage2D(target, level, internalformat, x, y, width, height, 0); michael@0: } else { michael@0: michael@0: // the rect doesn't fit in the framebuffer michael@0: michael@0: /*** first, we initialize the texture as black ***/ michael@0: michael@0: // first, compute the size of the buffer we should allocate to initialize the texture as black michael@0: michael@0: if (!ValidateTexInputData(LOCAL_GL_UNSIGNED_BYTE, -1, func)) michael@0: return; michael@0: michael@0: uint32_t texelSize = GetBitsPerTexel(internalformat, LOCAL_GL_UNSIGNED_BYTE) / 8; michael@0: michael@0: CheckedUint32 checked_neededByteLength = michael@0: GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment); michael@0: michael@0: if (!checked_neededByteLength.isValid()) michael@0: return ErrorInvalidOperation("%s: integer overflow computing the needed buffer size", info); michael@0: michael@0: uint32_t bytesNeeded = checked_neededByteLength.value(); michael@0: michael@0: // now that the size is known, create the buffer michael@0: michael@0: // We need some zero pages, because GL doesn't guarantee the michael@0: // contents of a texture allocated with nullptr data. michael@0: // Hopefully calloc will just mmap zero pages here. michael@0: void* tempZeroData = calloc(1, bytesNeeded); michael@0: if (!tempZeroData) michael@0: return ErrorOutOfMemory("%s: could not allocate %d bytes (for zero fill)", info, bytesNeeded); michael@0: michael@0: // now initialize the texture as black michael@0: michael@0: if (sub) michael@0: gl->fTexSubImage2D(target, level, 0, 0, width, height, michael@0: internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData); michael@0: else michael@0: gl->fTexImage2D(target, level, internalformat, width, height, michael@0: 0, internalformat, LOCAL_GL_UNSIGNED_BYTE, tempZeroData); michael@0: free(tempZeroData); michael@0: michael@0: // if we are completely outside of the framebuffer, we can exit now with our black texture michael@0: if ( x >= framebufferWidth michael@0: || x+width <= 0 michael@0: || y >= framebufferHeight michael@0: || y+height <= 0) michael@0: { michael@0: // we are completely outside of range, can exit now with buffer filled with zeros michael@0: return DummyFramebufferOperation(info); michael@0: } michael@0: michael@0: GLint actual_x = clamped(x, 0, framebufferWidth); michael@0: GLint actual_x_plus_width = clamped(x + width, 0, framebufferWidth); michael@0: GLsizei actual_width = actual_x_plus_width - actual_x; michael@0: GLint actual_xoffset = xoffset + actual_x - x; michael@0: michael@0: GLint actual_y = clamped(y, 0, framebufferHeight); michael@0: GLint actual_y_plus_height = clamped(y + height, 0, framebufferHeight); michael@0: GLsizei actual_height = actual_y_plus_height - actual_y; michael@0: GLint actual_yoffset = yoffset + actual_y - y; michael@0: michael@0: gl->fCopyTexSubImage2D(target, level, actual_xoffset, actual_yoffset, actual_x, actual_y, actual_width, actual_height); michael@0: } michael@0: } michael@0: michael@0: void michael@0: WebGLContext::CopyTexImage2D(GLenum target, michael@0: GLint level, michael@0: GLenum internalformat, michael@0: GLint x, michael@0: GLint y, michael@0: GLsizei width, michael@0: GLsizei height, michael@0: GLint border) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: // copyTexImage2D only generates textures with type = UNSIGNED_BYTE michael@0: const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage; michael@0: const GLenum format = internalformat; // WebGL/ES Format michael@0: const GLenum type = LOCAL_GL_UNSIGNED_BYTE; // WebGL/ES Format michael@0: michael@0: if (!ValidateTexImage(2, target, level, format, michael@0: 0, 0, 0, michael@0: width, height, 0, michael@0: border, format, type, michael@0: func)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: if (mBoundFramebuffer) { michael@0: if (!mBoundFramebuffer->CheckAndInitializeAttachments()) michael@0: return ErrorInvalidFramebufferOperation("copyTexImage2D: incomplete framebuffer"); michael@0: michael@0: GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT; michael@0: if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) { michael@0: return ErrorInvalidOperation("copyTexImage2D: Read source attachment doesn't have the" michael@0: " correct color/depth/stencil type."); michael@0: } michael@0: } else { michael@0: ClearBackbufferIfNeeded(); michael@0: } michael@0: michael@0: bool texFormatRequiresAlpha = format == LOCAL_GL_RGBA || michael@0: format == LOCAL_GL_ALPHA || michael@0: format == LOCAL_GL_LUMINANCE_ALPHA; michael@0: bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha() michael@0: : bool(gl->GetPixelFormat().alpha > 0); michael@0: if (texFormatRequiresAlpha && !fboFormatHasAlpha) michael@0: return ErrorInvalidOperation("copyTexImage2D: texture format requires an alpha channel " michael@0: "but the framebuffer doesn't have one"); michael@0: michael@0: // check if the memory size of this texture may change with this call michael@0: bool sizeMayChange = true; michael@0: WebGLTexture* tex = activeBoundTextureForTarget(target); michael@0: if (tex->HasImageInfoAt(target, level)) { michael@0: const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level); michael@0: michael@0: sizeMayChange = width != imageInfo.Width() || michael@0: height != imageInfo.Height() || michael@0: format != imageInfo.WebGLFormat() || michael@0: type != imageInfo.WebGLType(); michael@0: } michael@0: michael@0: if (sizeMayChange) michael@0: GetAndFlushUnderlyingGLErrors(); michael@0: michael@0: CopyTexSubImage2D_base(target, level, format, 0, 0, x, y, width, height, false); michael@0: michael@0: if (sizeMayChange) { michael@0: GLenum error = GetAndFlushUnderlyingGLErrors(); michael@0: if (error) { michael@0: GenerateWarning("copyTexImage2D generated error %s", ErrorName(error)); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: tex->SetImageInfo(target, level, width, height, format, type, michael@0: WebGLImageDataStatus::InitializedImageData); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::CopyTexSubImage2D(GLenum target, michael@0: GLint level, michael@0: GLint xoffset, michael@0: GLint yoffset, michael@0: GLint x, michael@0: GLint y, michael@0: GLsizei width, michael@0: GLsizei height) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: switch (target) { michael@0: case LOCAL_GL_TEXTURE_2D: michael@0: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: michael@0: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: michael@0: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: michael@0: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: michael@0: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: michael@0: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: michael@0: break; michael@0: default: michael@0: return ErrorInvalidEnumInfo("copyTexSubImage2D: target", target); michael@0: } michael@0: michael@0: if (level < 0) michael@0: return ErrorInvalidValue("copyTexSubImage2D: level may not be negative"); michael@0: michael@0: GLsizei maxTextureSize = MaxTextureSizeForTarget(target); michael@0: if (!(maxTextureSize >> level)) michael@0: return ErrorInvalidValue("copyTexSubImage2D: 2^level exceeds maximum texture size"); michael@0: michael@0: if (width < 0 || height < 0) michael@0: return ErrorInvalidValue("copyTexSubImage2D: width and height may not be negative"); michael@0: michael@0: if (xoffset < 0 || yoffset < 0) michael@0: return ErrorInvalidValue("copyTexSubImage2D: xoffset and yoffset may not be negative"); michael@0: michael@0: WebGLTexture *tex = activeBoundTextureForTarget(target); michael@0: if (!tex) michael@0: return ErrorInvalidOperation("copyTexSubImage2D: no texture bound to this target"); michael@0: michael@0: if (!tex->HasImageInfoAt(target, level)) michael@0: return ErrorInvalidOperation("copyTexSubImage2D: no texture image previously defined for this level and face"); michael@0: michael@0: const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level); michael@0: GLsizei texWidth = imageInfo.Width(); michael@0: GLsizei texHeight = imageInfo.Height(); michael@0: michael@0: if (xoffset + width > texWidth || xoffset + width < 0) michael@0: return ErrorInvalidValue("copyTexSubImage2D: xoffset+width is too large"); michael@0: michael@0: if (yoffset + height > texHeight || yoffset + height < 0) michael@0: return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large"); michael@0: michael@0: GLenum webGLFormat = imageInfo.WebGLFormat(); michael@0: if (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat)) michael@0: return ErrorInvalidOperation("copyTexSubImage2D: a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL isn't supported"); michael@0: michael@0: if (mBoundFramebuffer) { michael@0: if (!mBoundFramebuffer->CheckAndInitializeAttachments()) michael@0: return ErrorInvalidFramebufferOperation("copyTexSubImage2D: incomplete framebuffer"); michael@0: michael@0: GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT; michael@0: if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) { michael@0: return ErrorInvalidOperation("copyTexSubImage2D: Read source attachment doesn't have the" michael@0: " correct color/depth/stencil type."); michael@0: } michael@0: } else { michael@0: ClearBackbufferIfNeeded(); michael@0: } michael@0: michael@0: bool texFormatRequiresAlpha = FormatHasAlpha(webGLFormat); michael@0: bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha() michael@0: : bool(gl->GetPixelFormat().alpha > 0); michael@0: michael@0: if (texFormatRequiresAlpha && !fboFormatHasAlpha) michael@0: return ErrorInvalidOperation("copyTexSubImage2D: texture format requires an alpha channel " michael@0: "but the framebuffer doesn't have one"); michael@0: michael@0: if (imageInfo.HasUninitializedImageData()) { michael@0: tex->DoDeferredImageInitialization(target, level); michael@0: } michael@0: michael@0: return CopyTexSubImage2D_base(target, level, webGLFormat, xoffset, yoffset, x, y, width, height, true); michael@0: } michael@0: michael@0: michael@0: already_AddRefed michael@0: WebGLContext::CreateProgram() michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: nsRefPtr globj = new WebGLProgram(this); michael@0: return globj.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: WebGLContext::CreateShader(GLenum type) michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: michael@0: if (type != LOCAL_GL_VERTEX_SHADER && michael@0: type != LOCAL_GL_FRAGMENT_SHADER) michael@0: { michael@0: ErrorInvalidEnumInfo("createShader: type", type); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr shader = new WebGLShader(this, type); michael@0: return shader.forget(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::CullFace(GLenum face) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateFaceEnum(face, "cullFace")) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fCullFace(face); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObjectAllowDeletedOrNull("deleteFramebuffer", fbuf)) michael@0: return; michael@0: michael@0: if (!fbuf || fbuf->IsDeleted()) michael@0: return; michael@0: michael@0: fbuf->RequestDelete(); michael@0: michael@0: if (mBoundFramebuffer == fbuf) michael@0: BindFramebuffer(LOCAL_GL_FRAMEBUFFER, michael@0: static_cast(nullptr)); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer *rbuf) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObjectAllowDeletedOrNull("deleteRenderbuffer", rbuf)) michael@0: return; michael@0: michael@0: if (!rbuf || rbuf->IsDeleted()) michael@0: return; michael@0: michael@0: if (mBoundFramebuffer) michael@0: mBoundFramebuffer->DetachRenderbuffer(rbuf); michael@0: michael@0: // Invalidate framebuffer status cache michael@0: rbuf->NotifyFBsStatusChanged(); michael@0: michael@0: if (mBoundRenderbuffer == rbuf) michael@0: BindRenderbuffer(LOCAL_GL_RENDERBUFFER, michael@0: static_cast(nullptr)); michael@0: michael@0: rbuf->RequestDelete(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::DeleteTexture(WebGLTexture *tex) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObjectAllowDeletedOrNull("deleteTexture", tex)) michael@0: return; michael@0: michael@0: if (!tex || tex->IsDeleted()) michael@0: return; michael@0: michael@0: if (mBoundFramebuffer) michael@0: mBoundFramebuffer->DetachTexture(tex); michael@0: michael@0: // Invalidate framebuffer status cache michael@0: tex->NotifyFBsStatusChanged(); michael@0: michael@0: GLuint activeTexture = mActiveTexture; michael@0: for (int32_t i = 0; i < mGLMaxTextureUnits; i++) { michael@0: if ((tex->Target() == LOCAL_GL_TEXTURE_2D && mBound2DTextures[i] == tex) || michael@0: (tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP && mBoundCubeMapTextures[i] == tex)) michael@0: { michael@0: ActiveTexture(LOCAL_GL_TEXTURE0 + i); michael@0: BindTexture(tex->Target(), static_cast(nullptr)); michael@0: } michael@0: } michael@0: ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture); michael@0: michael@0: tex->RequestDelete(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::DeleteProgram(WebGLProgram *prog) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObjectAllowDeletedOrNull("deleteProgram", prog)) michael@0: return; michael@0: michael@0: if (!prog || prog->IsDeleted()) michael@0: return; michael@0: michael@0: prog->RequestDelete(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::DeleteShader(WebGLShader *shader) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObjectAllowDeletedOrNull("deleteShader", shader)) michael@0: return; michael@0: michael@0: if (!shader || shader->IsDeleted()) michael@0: return; michael@0: michael@0: shader->RequestDelete(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::DetachShader(WebGLProgram *program, WebGLShader *shader) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObject("detachShader: program", program) || michael@0: // it's valid to attempt to detach a deleted shader, since it's michael@0: // still a shader michael@0: !ValidateObjectAllowDeleted("detashShader: shader", shader)) michael@0: return; michael@0: michael@0: if (!program->DetachShader(shader)) michael@0: return ErrorInvalidOperation("detachShader: shader is not attached"); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::DepthFunc(GLenum func) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateComparisonEnum(func, "depthFunc")) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fDepthFunc(func); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::DepthRange(GLfloat zNear, GLfloat zFar) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (zNear > zFar) michael@0: return ErrorInvalidOperation("depthRange: the near value is greater than the far value!"); michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fDepthRange(zNear, zFar); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::FramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum rbtarget, WebGLRenderbuffer *wrb) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!mBoundFramebuffer) michael@0: return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0"); michael@0: michael@0: return mBoundFramebuffer->FramebufferRenderbuffer(target, attachment, rbtarget, wrb); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::FramebufferTexture2D(GLenum target, michael@0: GLenum attachment, michael@0: GLenum textarget, michael@0: WebGLTexture *tobj, michael@0: GLint level) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!mBoundFramebuffer) michael@0: return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0"); michael@0: michael@0: return mBoundFramebuffer->FramebufferTexture2D(target, attachment, textarget, tobj, level); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::FrontFace(GLenum mode) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: switch (mode) { michael@0: case LOCAL_GL_CW: michael@0: case LOCAL_GL_CCW: michael@0: break; michael@0: default: michael@0: return ErrorInvalidEnumInfo("frontFace: mode", mode); michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fFrontFace(mode); michael@0: } michael@0: michael@0: already_AddRefed michael@0: WebGLContext::GetActiveAttrib(WebGLProgram *prog, uint32_t index) michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: michael@0: if (!ValidateObject("getActiveAttrib: program", prog)) michael@0: return nullptr; michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: GLint len = 0; michael@0: GLuint progname = prog->GLName();; michael@0: gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len); michael@0: if (len == 0) michael@0: return nullptr; michael@0: michael@0: nsAutoArrayPtr name(new char[len]); michael@0: GLint attrsize = 0; michael@0: GLuint attrtype = 0; michael@0: michael@0: gl->fGetActiveAttrib(progname, index, len, &len, &attrsize, &attrtype, name); michael@0: if (attrsize == 0 || attrtype == 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCString reverseMappedName; michael@0: prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName); michael@0: michael@0: nsRefPtr retActiveInfo = michael@0: new WebGLActiveInfo(attrsize, attrtype, reverseMappedName); michael@0: return retActiveInfo.forget(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GenerateMipmap(GLenum target) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateTextureTargetEnum(target, "generateMipmap")) michael@0: return; michael@0: michael@0: WebGLTexture *tex = activeBoundTextureForTarget(target); michael@0: michael@0: if (!tex) michael@0: return ErrorInvalidOperation("generateMipmap: No texture is bound to this target."); michael@0: michael@0: GLenum imageTarget = (target == LOCAL_GL_TEXTURE_2D) ? LOCAL_GL_TEXTURE_2D michael@0: : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; michael@0: if (!tex->HasImageInfoAt(imageTarget, 0)) michael@0: { michael@0: return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined."); michael@0: } michael@0: michael@0: if (!tex->IsFirstImagePowerOfTwo()) michael@0: return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height."); michael@0: michael@0: GLenum webGLFormat = tex->ImageInfoAt(imageTarget, 0).WebGLFormat(); michael@0: if (IsTextureFormatCompressed(webGLFormat)) michael@0: return ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed."); michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) && michael@0: (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat))) michael@0: { michael@0: return ErrorInvalidOperation("generateMipmap: " michael@0: "A texture that has a base internal format of " michael@0: "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported"); michael@0: } michael@0: michael@0: if (!tex->AreAllLevel0ImageInfosEqual()) michael@0: return ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type."); michael@0: michael@0: tex->SetGeneratedMipmap(); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: if (gl->WorkAroundDriverBugs()) { michael@0: // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we michael@0: // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance michael@0: // overhead so we do it unconditionally. michael@0: // michael@0: // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105. michael@0: gl->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST_MIPMAP_NEAREST); michael@0: gl->fGenerateMipmap(target); michael@0: gl->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, tex->MinFilter()); michael@0: } else { michael@0: gl->fGenerateMipmap(target); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: WebGLContext::GetActiveUniform(WebGLProgram *prog, uint32_t index) michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: michael@0: if (!ValidateObject("getActiveUniform: program", prog)) michael@0: return nullptr; michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: GLint len = 0; michael@0: GLuint progname = prog->GLName(); michael@0: gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &len); michael@0: if (len == 0) michael@0: return nullptr; michael@0: michael@0: nsAutoArrayPtr name(new char[len]); michael@0: michael@0: GLint usize = 0; michael@0: GLuint utype = 0; michael@0: michael@0: gl->fGetActiveUniform(progname, index, len, &len, &usize, &utype, name); michael@0: if (len == 0 || usize == 0 || utype == 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCString reverseMappedName; michael@0: prog->ReverseMapIdentifier(nsDependentCString(name), &reverseMappedName); michael@0: michael@0: // OpenGL ES 2.0 specifies that if foo is a uniform array, GetActiveUniform returns its name as "foo[0]". michael@0: // See section 2.10 page 35 in the OpenGL ES 2.0.24 specification: michael@0: // michael@0: // > If the active uniform is an array, the uniform name returned in name will always michael@0: // > be the name of the uniform array appended with "[0]". michael@0: // michael@0: // There is no such requirement in the OpenGL (non-ES) spec and indeed we have OpenGL implementations returning michael@0: // "foo" instead of "foo[0]". So, when implementing WebGL on top of desktop OpenGL, we must check if the michael@0: // returned name ends in [0], and if it doesn't, append that. michael@0: // michael@0: // 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: // specs that it seems probable that some ES implementers will overlook it. Since the work-around is quite cheap, michael@0: // we do it unconditionally. michael@0: if (usize > 1 && reverseMappedName.CharAt(reverseMappedName.Length()-1) != ']') michael@0: reverseMappedName.AppendLiteral("[0]"); michael@0: michael@0: nsRefPtr retActiveInfo = michael@0: new WebGLActiveInfo(usize, utype, reverseMappedName); michael@0: return retActiveInfo.forget(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetAttachedShaders(WebGLProgram *prog, michael@0: Nullable< nsTArray > &retval) michael@0: { michael@0: retval.SetNull(); michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObjectAllowNull("getAttachedShaders", prog)) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: if (!prog) { michael@0: retval.SetNull(); michael@0: ErrorInvalidValue("getAttachedShaders: invalid program"); michael@0: } else if (prog->AttachedShaders().Length() == 0) { michael@0: retval.SetValue().TruncateLength(0); michael@0: } else { michael@0: retval.SetValue().AppendElements(prog->AttachedShaders()); michael@0: } michael@0: } michael@0: michael@0: GLint michael@0: WebGLContext::GetAttribLocation(WebGLProgram *prog, const nsAString& name) michael@0: { michael@0: if (IsContextLost()) michael@0: return -1; michael@0: michael@0: if (!ValidateObject("getAttribLocation: program", prog)) michael@0: return -1; michael@0: michael@0: if (!ValidateGLSLVariableName(name, "getAttribLocation")) michael@0: return -1; michael@0: michael@0: NS_LossyConvertUTF16toASCII cname(name); michael@0: nsCString mappedName; michael@0: prog->MapIdentifier(cname, &mappedName); michael@0: michael@0: GLuint progname = prog->GLName(); michael@0: michael@0: MakeContextCurrent(); michael@0: return gl->fGetAttribLocation(progname, mappedName.get()); michael@0: } michael@0: michael@0: JS::Value michael@0: WebGLContext::GetBufferParameter(GLenum target, GLenum pname) michael@0: { michael@0: if (IsContextLost()) michael@0: return JS::NullValue(); michael@0: michael@0: if (target != LOCAL_GL_ARRAY_BUFFER && target != LOCAL_GL_ELEMENT_ARRAY_BUFFER) { michael@0: ErrorInvalidEnumInfo("getBufferParameter: target", target); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: switch (pname) { michael@0: case LOCAL_GL_BUFFER_SIZE: michael@0: case LOCAL_GL_BUFFER_USAGE: michael@0: { michael@0: GLint i = 0; michael@0: gl->fGetBufferParameteriv(target, pname, &i); michael@0: if (pname == LOCAL_GL_BUFFER_SIZE) { michael@0: return JS::Int32Value(i); michael@0: } michael@0: michael@0: MOZ_ASSERT(pname == LOCAL_GL_BUFFER_USAGE); michael@0: return JS::NumberValue(uint32_t(i)); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: ErrorInvalidEnumInfo("getBufferParameter: parameter", pname); michael@0: } michael@0: michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: JS::Value michael@0: WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx, michael@0: GLenum target, michael@0: GLenum attachment, michael@0: GLenum pname, michael@0: ErrorResult& rv) michael@0: { michael@0: if (IsContextLost()) michael@0: return JS::NullValue(); michael@0: michael@0: if (target != LOCAL_GL_FRAMEBUFFER) { michael@0: ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: target", target); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: if (!mBoundFramebuffer) { michael@0: ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query framebuffer 0"); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: if (attachment != LOCAL_GL_DEPTH_ATTACHMENT && michael@0: attachment != LOCAL_GL_STENCIL_ATTACHMENT && michael@0: attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) michael@0: { michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) michael@0: { michael@0: if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 || michael@0: attachment >= GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + mGLMaxColorAttachments)) michael@0: { michael@0: ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: attachment", attachment); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: mBoundFramebuffer->EnsureColorAttachments(attachment - LOCAL_GL_COLOR_ATTACHMENT0); michael@0: } michael@0: else if (attachment != LOCAL_GL_COLOR_ATTACHMENT0) michael@0: { michael@0: ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: attachment", attachment); michael@0: return JS::NullValue(); michael@0: } michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: const WebGLFramebuffer::Attachment& fba = mBoundFramebuffer->GetAttachment(attachment); michael@0: michael@0: if (fba.Renderbuffer()) { michael@0: switch (pname) { michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT: michael@0: if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) { michael@0: const GLenum internalFormat = fba.Renderbuffer()->InternalFormat(); michael@0: return (internalFormat == LOCAL_GL_SRGB_EXT || michael@0: internalFormat == LOCAL_GL_SRGB_ALPHA_EXT || michael@0: internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT) ? michael@0: JS::NumberValue(uint32_t(LOCAL_GL_SRGB_EXT)) : michael@0: JS::NumberValue(uint32_t(LOCAL_GL_LINEAR)); michael@0: } michael@0: break; michael@0: michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: michael@0: return JS::NumberValue(uint32_t(LOCAL_GL_RENDERBUFFER)); michael@0: michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: michael@0: return WebGLObjectAsJSValue(cx, fba.Renderbuffer(), rv); michael@0: michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: { michael@0: if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) && michael@0: !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) michael@0: { michael@0: break; michael@0: } michael@0: michael@0: if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { michael@0: ErrorInvalidOperation("getFramebufferAttachmentParameter: Cannot get component" michael@0: " type of a depth-stencil attachment."); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: if (!fba.IsComplete()) michael@0: return JS::NumberValue(uint32_t(LOCAL_GL_NONE)); michael@0: michael@0: uint32_t ret = LOCAL_GL_NONE; michael@0: switch (fba.Renderbuffer()->InternalFormat()) { michael@0: case LOCAL_GL_RGBA4: michael@0: case LOCAL_GL_RGB5_A1: michael@0: case LOCAL_GL_RGB565: michael@0: case LOCAL_GL_SRGB8_ALPHA8: michael@0: ret = LOCAL_GL_UNSIGNED_NORMALIZED; michael@0: break; michael@0: case LOCAL_GL_RGB16F: michael@0: case LOCAL_GL_RGBA16F: michael@0: case LOCAL_GL_RGB32F: michael@0: case LOCAL_GL_RGBA32F: michael@0: ret = LOCAL_GL_FLOAT; michael@0: break; michael@0: case LOCAL_GL_DEPTH_COMPONENT16: michael@0: case LOCAL_GL_STENCIL_INDEX8: michael@0: ret = LOCAL_GL_UNSIGNED_INT; michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "Unhandled RB component type."); michael@0: break; michael@0: } michael@0: return JS::NumberValue(uint32_t(ret)); michael@0: } michael@0: } michael@0: michael@0: ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname); michael@0: return JS::NullValue(); michael@0: } else if (fba.Texture()) { michael@0: switch (pname) { michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT: michael@0: if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) { michael@0: const GLenum webGLFormat = michael@0: fba.Texture()->ImageInfoBase().WebGLFormat(); michael@0: return (webGLFormat == LOCAL_GL_SRGB || michael@0: webGLFormat == LOCAL_GL_SRGB_ALPHA) ? michael@0: JS::NumberValue(uint32_t(LOCAL_GL_SRGB)) : michael@0: JS::NumberValue(uint32_t(LOCAL_GL_LINEAR)); michael@0: } michael@0: break; michael@0: michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: michael@0: return JS::NumberValue(uint32_t(LOCAL_GL_TEXTURE)); michael@0: michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: michael@0: return WebGLObjectAsJSValue(cx, fba.Texture(), rv); michael@0: michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: michael@0: return JS::Int32Value(fba.TexImageLevel()); michael@0: michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: { michael@0: GLenum face = fba.TexImageTarget(); michael@0: if (face == LOCAL_GL_TEXTURE_2D) michael@0: face = 0; michael@0: return JS::Int32Value(face); michael@0: } michael@0: michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: { michael@0: if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) && michael@0: !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) michael@0: { michael@0: break; michael@0: } michael@0: michael@0: if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { michael@0: ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot component" michael@0: " type of depth-stencil attachments."); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: if (!fba.IsComplete()) michael@0: return JS::NumberValue(uint32_t(LOCAL_GL_NONE)); michael@0: michael@0: uint32_t ret = LOCAL_GL_NONE; michael@0: GLenum type = fba.Texture()->ImageInfoAt(fba.TexImageTarget(), michael@0: fba.TexImageLevel()).WebGLType(); michael@0: switch (type) { michael@0: case LOCAL_GL_UNSIGNED_BYTE: michael@0: case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_6_5: michael@0: ret = LOCAL_GL_UNSIGNED_NORMALIZED; michael@0: break; michael@0: case LOCAL_GL_FLOAT: michael@0: case LOCAL_GL_HALF_FLOAT_OES: michael@0: ret = LOCAL_GL_FLOAT; michael@0: break; michael@0: case LOCAL_GL_UNSIGNED_SHORT: michael@0: case LOCAL_GL_UNSIGNED_INT: michael@0: ret = LOCAL_GL_UNSIGNED_INT; michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "Unhandled RB component type."); michael@0: break; michael@0: } michael@0: return JS::NumberValue(uint32_t(ret)); michael@0: } michael@0: } michael@0: michael@0: ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname); michael@0: return JS::NullValue(); michael@0: } else { michael@0: switch (pname) { michael@0: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: michael@0: return JS::NumberValue(uint32_t(LOCAL_GL_NONE)); michael@0: michael@0: default: michael@0: ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname); michael@0: return JS::NullValue(); michael@0: } michael@0: } michael@0: michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: JS::Value michael@0: WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname) michael@0: { michael@0: if (IsContextLost()) michael@0: return JS::NullValue(); michael@0: michael@0: if (target != LOCAL_GL_RENDERBUFFER) { michael@0: ErrorInvalidEnumInfo("getRenderbufferParameter: target", target); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: if (!mBoundRenderbuffer) { michael@0: ErrorInvalidOperation("getRenderbufferParameter: no render buffer is bound"); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: switch (pname) { michael@0: case LOCAL_GL_RENDERBUFFER_WIDTH: michael@0: case LOCAL_GL_RENDERBUFFER_HEIGHT: michael@0: case LOCAL_GL_RENDERBUFFER_RED_SIZE: michael@0: case LOCAL_GL_RENDERBUFFER_GREEN_SIZE: michael@0: case LOCAL_GL_RENDERBUFFER_BLUE_SIZE: michael@0: case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: michael@0: case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: michael@0: case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: michael@0: { michael@0: // RB emulation means we have to ask the RB itself. michael@0: GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname); michael@0: return JS::Int32Value(i); michael@0: } michael@0: case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: michael@0: { michael@0: return JS::NumberValue(mBoundRenderbuffer->InternalFormat()); michael@0: } michael@0: default: michael@0: ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname); michael@0: } michael@0: michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: WebGLContext::CreateTexture() michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: nsRefPtr globj = new WebGLTexture(this); michael@0: return globj.forget(); michael@0: } michael@0: michael@0: static GLenum michael@0: GetAndClearError(GLenum* errorVar) michael@0: { michael@0: MOZ_ASSERT(errorVar); michael@0: GLenum ret = *errorVar; michael@0: *errorVar = LOCAL_GL_NO_ERROR; michael@0: return ret; michael@0: } michael@0: michael@0: GLenum michael@0: WebGLContext::GetError() michael@0: { michael@0: /* WebGL 1.0: Section 5.14.3: Setting and getting state: michael@0: * If the context's webgl context lost flag is set, returns michael@0: * CONTEXT_LOST_WEBGL the first time this method is called. michael@0: * Afterward, returns NO_ERROR until the context has been michael@0: * restored. michael@0: * michael@0: * WEBGL_lose_context: michael@0: * [When this extension is enabled: ] loseContext and michael@0: * restoreContext are allowed to generate INVALID_OPERATION errors michael@0: * even when the context is lost. michael@0: */ michael@0: michael@0: if (IsContextLost()) { michael@0: if (mEmitContextLostErrorOnce) { michael@0: mEmitContextLostErrorOnce = false; michael@0: return LOCAL_GL_CONTEXT_LOST; michael@0: } michael@0: // Don't return yet, since WEBGL_lose_contexts contradicts the michael@0: // original spec, and allows error generation while lost. michael@0: } michael@0: michael@0: GLenum err = GetAndClearError(&mWebGLError); michael@0: if (err != LOCAL_GL_NO_ERROR) michael@0: return err; michael@0: michael@0: if (IsContextLost()) michael@0: return LOCAL_GL_NO_ERROR; michael@0: michael@0: // Either no WebGL-side error, or it's already been cleared. michael@0: // UnderlyingGL-side errors, now. michael@0: michael@0: MakeContextCurrent(); michael@0: GetAndFlushUnderlyingGLErrors(); michael@0: michael@0: err = GetAndClearError(&mUnderlyingGLError); michael@0: return err; michael@0: } michael@0: michael@0: JS::Value michael@0: WebGLContext::GetProgramParameter(WebGLProgram *prog, GLenum pname) michael@0: { michael@0: if (IsContextLost()) michael@0: return JS::NullValue(); michael@0: michael@0: if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog)) michael@0: return JS::NullValue(); michael@0: michael@0: GLuint progname = prog->GLName(); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: switch (pname) { michael@0: case LOCAL_GL_ATTACHED_SHADERS: michael@0: case LOCAL_GL_ACTIVE_UNIFORMS: michael@0: case LOCAL_GL_ACTIVE_ATTRIBUTES: michael@0: { michael@0: GLint i = 0; michael@0: gl->fGetProgramiv(progname, pname, &i); michael@0: return JS::Int32Value(i); michael@0: } michael@0: case LOCAL_GL_DELETE_STATUS: michael@0: return JS::BooleanValue(prog->IsDeleteRequested()); michael@0: case LOCAL_GL_LINK_STATUS: michael@0: { michael@0: return JS::BooleanValue(prog->LinkStatus()); michael@0: } michael@0: case LOCAL_GL_VALIDATE_STATUS: michael@0: { michael@0: GLint i = 0; michael@0: #ifdef XP_MACOSX michael@0: // See comment in ValidateProgram below. michael@0: if (gl->WorkAroundDriverBugs()) michael@0: i = 1; michael@0: else michael@0: gl->fGetProgramiv(progname, pname, &i); michael@0: #else michael@0: gl->fGetProgramiv(progname, pname, &i); michael@0: #endif michael@0: return JS::BooleanValue(bool(i)); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: ErrorInvalidEnumInfo("getProgramParameter: parameter", pname); michael@0: } michael@0: michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetProgramInfoLog(WebGLProgram *prog, nsAString& retval) michael@0: { michael@0: nsAutoCString s; michael@0: GetProgramInfoLog(prog, s); michael@0: if (s.IsVoid()) michael@0: retval.SetIsVoid(true); michael@0: else michael@0: CopyASCIItoUTF16(s, retval); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetProgramInfoLog(WebGLProgram *prog, nsACString& retval) michael@0: { michael@0: if (IsContextLost()) michael@0: { michael@0: retval.SetIsVoid(true); michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateObject("getProgramInfoLog: program", prog)) { michael@0: retval.Truncate(); michael@0: return; michael@0: } michael@0: michael@0: GLuint progname = prog->GLName(); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: GLint k = -1; michael@0: gl->fGetProgramiv(progname, LOCAL_GL_INFO_LOG_LENGTH, &k); michael@0: if (k == -1) { michael@0: // If GetProgramiv doesn't modify |k|, michael@0: // it's because there was a GL error. michael@0: // GetProgramInfoLog should return null on error. (Bug 746740) michael@0: retval.SetIsVoid(true); michael@0: return; michael@0: } michael@0: michael@0: if (k == 0) { michael@0: retval.Truncate(); michael@0: return; michael@0: } michael@0: michael@0: retval.SetCapacity(k); michael@0: gl->fGetProgramInfoLog(progname, k, &k, (char*) retval.BeginWriting()); michael@0: retval.SetLength(k); michael@0: } michael@0: michael@0: // here we have to support all pnames with both int and float params. michael@0: // See this discussion: michael@0: // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html michael@0: void WebGLContext::TexParameter_base(GLenum target, GLenum pname, michael@0: GLint *intParamPtr, michael@0: GLfloat *floatParamPtr) michael@0: { michael@0: MOZ_ASSERT(intParamPtr || floatParamPtr); michael@0: michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: GLint intParam = intParamPtr ? *intParamPtr : GLint(*floatParamPtr); michael@0: GLfloat floatParam = floatParamPtr ? *floatParamPtr : GLfloat(*intParamPtr); michael@0: michael@0: if (!ValidateTextureTargetEnum(target, "texParameter: target")) michael@0: return; michael@0: michael@0: WebGLTexture *tex = activeBoundTextureForTarget(target); michael@0: if (!tex) michael@0: return ErrorInvalidOperation("texParameter: no texture is bound to this target"); michael@0: michael@0: bool pnameAndParamAreIncompatible = false; michael@0: bool paramValueInvalid = false; michael@0: michael@0: switch (pname) { michael@0: case LOCAL_GL_TEXTURE_MIN_FILTER: michael@0: switch (intParam) { michael@0: case LOCAL_GL_NEAREST: michael@0: case LOCAL_GL_LINEAR: michael@0: case LOCAL_GL_NEAREST_MIPMAP_NEAREST: michael@0: case LOCAL_GL_LINEAR_MIPMAP_NEAREST: michael@0: case LOCAL_GL_NEAREST_MIPMAP_LINEAR: michael@0: case LOCAL_GL_LINEAR_MIPMAP_LINEAR: michael@0: tex->SetMinFilter(intParam); michael@0: break; michael@0: default: michael@0: pnameAndParamAreIncompatible = true; michael@0: } michael@0: break; michael@0: case LOCAL_GL_TEXTURE_MAG_FILTER: michael@0: switch (intParam) { michael@0: case LOCAL_GL_NEAREST: michael@0: case LOCAL_GL_LINEAR: michael@0: tex->SetMagFilter(intParam); michael@0: break; michael@0: default: michael@0: pnameAndParamAreIncompatible = true; michael@0: } michael@0: break; michael@0: case LOCAL_GL_TEXTURE_WRAP_S: michael@0: switch (intParam) { michael@0: case LOCAL_GL_CLAMP_TO_EDGE: michael@0: case LOCAL_GL_MIRRORED_REPEAT: michael@0: case LOCAL_GL_REPEAT: michael@0: tex->SetWrapS(intParam); michael@0: break; michael@0: default: michael@0: pnameAndParamAreIncompatible = true; michael@0: } michael@0: break; michael@0: case LOCAL_GL_TEXTURE_WRAP_T: michael@0: switch (intParam) { michael@0: case LOCAL_GL_CLAMP_TO_EDGE: michael@0: case LOCAL_GL_MIRRORED_REPEAT: michael@0: case LOCAL_GL_REPEAT: michael@0: tex->SetWrapT(intParam); michael@0: break; michael@0: default: michael@0: pnameAndParamAreIncompatible = true; michael@0: } michael@0: break; michael@0: case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: michael@0: if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) { michael@0: if (floatParamPtr && floatParam < 1.f) michael@0: paramValueInvalid = true; michael@0: else if (intParamPtr && intParam < 1) michael@0: paramValueInvalid = true; michael@0: } michael@0: else michael@0: pnameAndParamAreIncompatible = true; michael@0: break; michael@0: default: michael@0: return ErrorInvalidEnumInfo("texParameter: pname", pname); michael@0: } michael@0: michael@0: if (pnameAndParamAreIncompatible) { michael@0: if (intParamPtr) michael@0: return ErrorInvalidEnum("texParameteri: pname %x and param %x (decimal %d) are mutually incompatible", michael@0: pname, intParam, intParam); michael@0: else michael@0: return ErrorInvalidEnum("texParameterf: pname %x and param %g are mutually incompatible", michael@0: pname, floatParam); michael@0: } else if (paramValueInvalid) { michael@0: if (intParamPtr) michael@0: return ErrorInvalidValue("texParameteri: pname %x and param %x (decimal %d) is invalid", michael@0: pname, intParam, intParam); michael@0: else michael@0: return ErrorInvalidValue("texParameterf: pname %x and param %g is invalid", michael@0: pname, floatParam); michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: if (intParamPtr) michael@0: gl->fTexParameteri(target, pname, intParam); michael@0: else michael@0: gl->fTexParameterf(target, pname, floatParam); michael@0: } michael@0: michael@0: JS::Value michael@0: WebGLContext::GetTexParameter(GLenum target, GLenum pname) michael@0: { michael@0: if (IsContextLost()) michael@0: return JS::NullValue(); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: if (!ValidateTextureTargetEnum(target, "getTexParameter: target")) michael@0: return JS::NullValue(); michael@0: michael@0: if (!activeBoundTextureForTarget(target)) { michael@0: ErrorInvalidOperation("getTexParameter: no texture bound"); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: switch (pname) { michael@0: case LOCAL_GL_TEXTURE_MIN_FILTER: michael@0: case LOCAL_GL_TEXTURE_MAG_FILTER: michael@0: case LOCAL_GL_TEXTURE_WRAP_S: michael@0: case LOCAL_GL_TEXTURE_WRAP_T: michael@0: { michael@0: GLint i = 0; michael@0: gl->fGetTexParameteriv(target, pname, &i); michael@0: return JS::NumberValue(uint32_t(i)); michael@0: } michael@0: case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: michael@0: if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) { michael@0: GLfloat f = 0.f; michael@0: gl->fGetTexParameterfv(target, pname, &f); michael@0: return JS::DoubleValue(f); michael@0: } michael@0: michael@0: ErrorInvalidEnumInfo("getTexParameter: parameter", pname); michael@0: break; michael@0: michael@0: default: michael@0: ErrorInvalidEnumInfo("getTexParameter: parameter", pname); michael@0: } michael@0: michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: JS::Value michael@0: WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog, michael@0: WebGLUniformLocation *location) michael@0: { michael@0: if (IsContextLost()) michael@0: return JS::NullValue(); michael@0: michael@0: if (!ValidateObject("getUniform: program", prog)) michael@0: return JS::NullValue(); michael@0: michael@0: if (!ValidateObject("getUniform: location", location)) michael@0: return JS::NullValue(); michael@0: michael@0: if (location->Program() != prog) { michael@0: ErrorInvalidValue("getUniform: this uniform location corresponds to another program"); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: if (location->ProgramGeneration() != prog->Generation()) { michael@0: ErrorInvalidOperation("getUniform: this uniform location is obsolete since the program has been relinked"); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: GLuint progname = prog->GLName(); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: GLint uniforms = 0; michael@0: GLint uniformNameMaxLength = 0; michael@0: gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS, &uniforms); michael@0: gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniformNameMaxLength); michael@0: michael@0: // we now need the type info to switch between fGetUniformfv and fGetUniformiv michael@0: // the only way to get that is to iterate through all active uniforms by index until michael@0: // one matches the given uniform location. michael@0: GLenum uniformType = 0; michael@0: nsAutoArrayPtr uniformName(new GLchar[uniformNameMaxLength]); michael@0: // this buffer has 16 more bytes to be able to store [index] at the end. michael@0: nsAutoArrayPtr uniformNameBracketIndex(new GLchar[uniformNameMaxLength + 16]); michael@0: michael@0: GLint index; michael@0: for (index = 0; index < uniforms; ++index) { michael@0: GLsizei length; michael@0: GLint size; michael@0: gl->fGetActiveUniform(progname, index, uniformNameMaxLength, &length, michael@0: &size, &uniformType, uniformName); michael@0: if (gl->fGetUniformLocation(progname, uniformName) == location->Location()) michael@0: break; michael@0: michael@0: // now we handle the case of array uniforms. In that case, fGetActiveUniform returned as 'size' michael@0: // the biggest index used plus one, so we need to loop over that. The 0 index has already been handled above, michael@0: // so we can start at one. For each index, we construct the string uniformName + "[" + index + "]". michael@0: if (size > 1) { michael@0: bool found_it = false; michael@0: if (uniformName[length - 1] == ']') { // if uniformName ends in [0] michael@0: // remove the [0] at the end michael@0: length -= 3; michael@0: uniformName[length] = 0; michael@0: } michael@0: for (GLint arrayIndex = 1; arrayIndex < size; arrayIndex++) { michael@0: sprintf(uniformNameBracketIndex.get(), "%s[%d]", uniformName.get(), arrayIndex); michael@0: if (gl->fGetUniformLocation(progname, uniformNameBracketIndex) == location->Location()) { michael@0: found_it = true; michael@0: break; michael@0: } michael@0: } michael@0: if (found_it) break; michael@0: } michael@0: } michael@0: michael@0: if (index == uniforms) { michael@0: GenerateWarning("getUniform: internal error: hit an OpenGL driver bug"); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: GLenum baseType; michael@0: GLint unitSize; michael@0: if (!BaseTypeAndSizeFromUniformType(uniformType, &baseType, &unitSize)) { michael@0: GenerateWarning("getUniform: internal error: unknown uniform type 0x%x", uniformType); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: // this should never happen michael@0: if (unitSize > 16) { michael@0: GenerateWarning("getUniform: internal error: unexpected uniform unit size %d", unitSize); michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: if (baseType == LOCAL_GL_FLOAT) { michael@0: GLfloat fv[16] = { GLfloat(0) }; michael@0: gl->fGetUniformfv(progname, location->Location(), fv); michael@0: if (unitSize == 1) { michael@0: return JS::DoubleValue(fv[0]); michael@0: } else { michael@0: JSObject* obj = Float32Array::Create(cx, this, unitSize, fv); michael@0: if (!obj) { michael@0: ErrorOutOfMemory("getUniform: out of memory"); michael@0: return JS::NullValue(); michael@0: } michael@0: return JS::ObjectOrNullValue(obj); michael@0: } michael@0: } else if (baseType == LOCAL_GL_INT) { michael@0: GLint iv[16] = { 0 }; michael@0: gl->fGetUniformiv(progname, location->Location(), iv); michael@0: if (unitSize == 1) { michael@0: return JS::Int32Value(iv[0]); michael@0: } else { michael@0: JSObject* obj = Int32Array::Create(cx, this, unitSize, iv); michael@0: if (!obj) { michael@0: ErrorOutOfMemory("getUniform: out of memory"); michael@0: return JS::NullValue(); michael@0: } michael@0: return JS::ObjectOrNullValue(obj); michael@0: } michael@0: } else if (baseType == LOCAL_GL_BOOL) { michael@0: GLint iv[16] = { 0 }; michael@0: gl->fGetUniformiv(progname, location->Location(), iv); michael@0: if (unitSize == 1) { michael@0: return JS::BooleanValue(iv[0] ? true : false); michael@0: } else { michael@0: bool uv[16]; michael@0: for (int k = 0; k < unitSize; k++) michael@0: uv[k] = iv[k]; michael@0: JS::Rooted val(cx); michael@0: // Be careful: we don't want to convert all of |uv|! michael@0: if (!ToJSValue(cx, uv, unitSize, &val)) { michael@0: ErrorOutOfMemory("getUniform: out of memory"); michael@0: return JS::NullValue(); michael@0: } michael@0: return val; michael@0: } michael@0: } michael@0: michael@0: // Else preserving behavior, but I'm not sure this is correct per spec michael@0: return JS::UndefinedValue(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: WebGLContext::GetUniformLocation(WebGLProgram *prog, const nsAString& name) michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: michael@0: if (!ValidateObject("getUniformLocation: program", prog)) michael@0: return nullptr; michael@0: michael@0: if (!ValidateGLSLVariableName(name, "getUniformLocation")) michael@0: return nullptr; michael@0: michael@0: NS_LossyConvertUTF16toASCII cname(name); michael@0: nsCString mappedName; michael@0: prog->MapIdentifier(cname, &mappedName); michael@0: michael@0: GLuint progname = prog->GLName(); michael@0: MakeContextCurrent(); michael@0: GLint intlocation = gl->fGetUniformLocation(progname, mappedName.get()); michael@0: michael@0: nsRefPtr loc; michael@0: if (intlocation >= 0) { michael@0: WebGLUniformInfo info = prog->GetUniformInfoForMappedIdentifier(mappedName); michael@0: loc = new WebGLUniformLocation(this, michael@0: prog, michael@0: intlocation, michael@0: info); michael@0: } michael@0: return loc.forget(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Hint(GLenum target, GLenum mode) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: bool isValid = false; michael@0: michael@0: switch (target) { michael@0: case LOCAL_GL_GENERATE_MIPMAP_HINT: michael@0: isValid = true; michael@0: break; michael@0: case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: michael@0: if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) michael@0: isValid = true; michael@0: break; michael@0: } michael@0: michael@0: if (!isValid) michael@0: return ErrorInvalidEnum("hint: invalid hint"); michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fHint(target, mode); michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::IsFramebuffer(WebGLFramebuffer *fb) michael@0: { michael@0: if (IsContextLost()) michael@0: return false; michael@0: michael@0: return ValidateObjectAllowDeleted("isFramebuffer", fb) && michael@0: !fb->IsDeleted() && michael@0: fb->HasEverBeenBound(); michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::IsProgram(WebGLProgram *prog) michael@0: { michael@0: if (IsContextLost()) michael@0: return false; michael@0: michael@0: return ValidateObjectAllowDeleted("isProgram", prog) && !prog->IsDeleted(); michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::IsRenderbuffer(WebGLRenderbuffer *rb) michael@0: { michael@0: if (IsContextLost()) michael@0: return false; michael@0: michael@0: return ValidateObjectAllowDeleted("isRenderBuffer", rb) && michael@0: !rb->IsDeleted() && michael@0: rb->HasEverBeenBound(); michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::IsShader(WebGLShader *shader) michael@0: { michael@0: if (IsContextLost()) michael@0: return false; michael@0: michael@0: return ValidateObjectAllowDeleted("isShader", shader) && michael@0: !shader->IsDeleted(); michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::IsTexture(WebGLTexture *tex) michael@0: { michael@0: if (IsContextLost()) michael@0: return false; michael@0: michael@0: return ValidateObjectAllowDeleted("isTexture", tex) && michael@0: !tex->IsDeleted() && michael@0: tex->HasEverBeenBound(); michael@0: } michael@0: michael@0: // Try to bind an attribute that is an array to location 0: michael@0: bool WebGLContext::BindArrayAttribToLocation0(WebGLProgram *program) michael@0: { michael@0: if (mBoundVertexArray->IsAttribArrayEnabled(0)) { michael@0: return false; michael@0: } michael@0: michael@0: GLint leastArrayLocation = -1; michael@0: michael@0: std::map::iterator itr; michael@0: for (itr = program->mActiveAttribMap.begin(); michael@0: itr != program->mActiveAttribMap.end(); michael@0: itr++) { michael@0: int32_t index = itr->first; michael@0: if (mBoundVertexArray->IsAttribArrayEnabled(index) && michael@0: index < leastArrayLocation) michael@0: { michael@0: leastArrayLocation = index; michael@0: } michael@0: } michael@0: michael@0: if (leastArrayLocation > 0) { michael@0: nsCString& attrName = program->mActiveAttribMap.find(leastArrayLocation)->second; michael@0: const char* attrNameCStr = attrName.get(); michael@0: gl->fBindAttribLocation(program->GLName(), 0, attrNameCStr); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::LinkProgram(WebGLProgram *program) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObject("linkProgram", program)) michael@0: return; michael@0: michael@0: InvalidateBufferFetching(); // we do it early in this function michael@0: // as some of the validation below changes program state michael@0: michael@0: GLuint progname = program->GLName(); michael@0: michael@0: if (!program->NextGeneration()) { michael@0: // XXX throw? michael@0: return; michael@0: } michael@0: michael@0: if (!program->HasBothShaderTypesAttached()) { michael@0: GenerateWarning("linkProgram: this program doesn't have both a vertex shader" michael@0: " and a fragment shader"); michael@0: program->SetLinkStatus(false); michael@0: return; michael@0: } michael@0: michael@0: // bug 777028 michael@0: // Mesa can't handle more than 16 samplers per program, counting each array entry. michael@0: if (gl->WorkAroundDriverBugs() && michael@0: mIsMesa && michael@0: program->UpperBoundNumSamplerUniforms() > 16) michael@0: { michael@0: GenerateWarning("Programs with more than 16 samplers are disallowed on Mesa drivers " "to avoid a Mesa crasher."); michael@0: program->SetLinkStatus(false); michael@0: return; michael@0: } michael@0: michael@0: bool updateInfoSucceeded = false; michael@0: GLint ok = 0; michael@0: if (gl->WorkAroundDriverBugs() && michael@0: program->HasBadShaderAttached()) michael@0: { michael@0: // it's a common driver bug, caught by program-test.html, that linkProgram doesn't michael@0: // correctly preserve the state of an in-use program that has been attached a bad shader michael@0: // see bug 777883 michael@0: ok = false; michael@0: } else { michael@0: MakeContextCurrent(); michael@0: gl->fLinkProgram(progname); michael@0: gl->fGetProgramiv(progname, LOCAL_GL_LINK_STATUS, &ok); michael@0: michael@0: if (ok) { michael@0: updateInfoSucceeded = program->UpdateInfo(); michael@0: program->SetLinkStatus(updateInfoSucceeded); michael@0: michael@0: if (BindArrayAttribToLocation0(program)) { michael@0: GenerateWarning("linkProgram: relinking program to make attrib0 an " michael@0: "array."); michael@0: gl->fLinkProgram(progname); michael@0: gl->fGetProgramiv(progname, LOCAL_GL_LINK_STATUS, &ok); michael@0: if (ok) { michael@0: updateInfoSucceeded = program->UpdateInfo(); michael@0: program->SetLinkStatus(updateInfoSucceeded); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (ok) { michael@0: // Bug 750527 michael@0: if (gl->WorkAroundDriverBugs() && michael@0: updateInfoSucceeded && michael@0: gl->Vendor() == gl::GLVendor::NVIDIA) michael@0: { michael@0: if (program == mCurrentProgram) michael@0: gl->fUseProgram(progname); michael@0: } michael@0: } else { michael@0: program->SetLinkStatus(false); michael@0: michael@0: if (ShouldGenerateWarnings()) { michael@0: michael@0: // report shader/program infoLogs as warnings. michael@0: // note that shader compilation errors can be deferred to linkProgram, michael@0: // which is why we can't do anything in compileShader. In practice we could michael@0: // report in compileShader the translation errors generated by ANGLE, michael@0: // but it seems saner to keep a single way of obtaining shader infologs. michael@0: michael@0: nsAutoCString log; michael@0: michael@0: bool alreadyReportedShaderInfoLog = false; michael@0: michael@0: for (size_t i = 0; i < program->AttachedShaders().Length(); i++) { michael@0: michael@0: WebGLShader* shader = program->AttachedShaders()[i]; michael@0: michael@0: if (shader->CompileStatus()) michael@0: continue; michael@0: michael@0: const char *shaderTypeName = nullptr; michael@0: if (shader->ShaderType() == LOCAL_GL_VERTEX_SHADER) { michael@0: shaderTypeName = "vertex"; michael@0: } else if (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) { michael@0: shaderTypeName = "fragment"; michael@0: } else { michael@0: // should have been validated earlier michael@0: MOZ_ASSERT(false); michael@0: shaderTypeName = ""; michael@0: } michael@0: michael@0: GetShaderInfoLog(shader, log); michael@0: michael@0: GenerateWarning("linkProgram: a %s shader used in this program failed to " michael@0: "compile, with this log:\n%s\n", michael@0: shaderTypeName, michael@0: log.get()); michael@0: alreadyReportedShaderInfoLog = true; michael@0: } michael@0: michael@0: if (!alreadyReportedShaderInfoLog) { michael@0: GetProgramInfoLog(program, log); michael@0: if (!log.IsEmpty()) { michael@0: GenerateWarning("linkProgram failed, with this log:\n%s\n", michael@0: log.get()); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: WebGLContext::PixelStorei(GLenum pname, GLint param) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: switch (pname) { michael@0: case UNPACK_FLIP_Y_WEBGL: michael@0: mPixelStoreFlipY = (param != 0); michael@0: break; michael@0: case UNPACK_PREMULTIPLY_ALPHA_WEBGL: michael@0: mPixelStorePremultiplyAlpha = (param != 0); michael@0: break; michael@0: case UNPACK_COLORSPACE_CONVERSION_WEBGL: michael@0: if (param == LOCAL_GL_NONE || param == BROWSER_DEFAULT_WEBGL) michael@0: mPixelStoreColorspaceConversion = param; michael@0: else michael@0: return ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter", param); michael@0: break; michael@0: case LOCAL_GL_PACK_ALIGNMENT: michael@0: case LOCAL_GL_UNPACK_ALIGNMENT: michael@0: if (param != 1 && michael@0: param != 2 && michael@0: param != 4 && michael@0: param != 8) michael@0: return ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value"); michael@0: if (pname == LOCAL_GL_PACK_ALIGNMENT) michael@0: mPixelStorePackAlignment = param; michael@0: else if (pname == LOCAL_GL_UNPACK_ALIGNMENT) michael@0: mPixelStoreUnpackAlignment = param; michael@0: MakeContextCurrent(); michael@0: gl->fPixelStorei(pname, param); michael@0: break; michael@0: default: michael@0: return ErrorInvalidEnumInfo("pixelStorei: parameter", pname); michael@0: } michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, michael@0: GLsizei height, GLenum format, michael@0: GLenum type, const Nullable &pixels, michael@0: ErrorResult& rv) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) { michael@0: GenerateWarning("readPixels: Not allowed"); michael@0: return rv.Throw(NS_ERROR_DOM_SECURITY_ERR); michael@0: } michael@0: michael@0: if (width < 0 || height < 0) michael@0: return ErrorInvalidValue("readPixels: negative size passed"); michael@0: michael@0: if (pixels.IsNull()) michael@0: return ErrorInvalidValue("readPixels: null destination buffer"); michael@0: michael@0: const WebGLRectangleObject* framebufferRect = CurValidFBRectObject(); michael@0: GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0; michael@0: GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0; michael@0: michael@0: uint32_t channels = 0; michael@0: michael@0: // Check the format param michael@0: switch (format) { michael@0: case LOCAL_GL_ALPHA: michael@0: channels = 1; michael@0: break; michael@0: case LOCAL_GL_RGB: michael@0: channels = 3; michael@0: break; michael@0: case LOCAL_GL_RGBA: michael@0: channels = 4; michael@0: break; michael@0: default: michael@0: return ErrorInvalidEnum("readPixels: Bad format"); michael@0: } michael@0: michael@0: uint32_t bytesPerPixel = 0; michael@0: int requiredDataType = 0; michael@0: michael@0: // Check the type param michael@0: bool isReadTypeValid = false; michael@0: bool isReadTypeFloat = false; michael@0: switch (type) { michael@0: case LOCAL_GL_UNSIGNED_BYTE: michael@0: isReadTypeValid = true; michael@0: bytesPerPixel = 1*channels; michael@0: requiredDataType = js::ArrayBufferView::TYPE_UINT8; michael@0: break; michael@0: case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_6_5: michael@0: isReadTypeValid = true; michael@0: bytesPerPixel = 2; michael@0: requiredDataType = js::ArrayBufferView::TYPE_UINT16; michael@0: break; michael@0: case LOCAL_GL_FLOAT: michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float) || michael@0: IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float)) michael@0: { michael@0: isReadTypeValid = true; michael@0: isReadTypeFloat = true; michael@0: bytesPerPixel = 4*channels; michael@0: requiredDataType = js::ArrayBufferView::TYPE_FLOAT32; michael@0: } michael@0: break; michael@0: } michael@0: if (!isReadTypeValid) michael@0: return ErrorInvalidEnum("readPixels: Bad type", type); michael@0: michael@0: const ArrayBufferView& pixbuf = pixels.Value(); michael@0: int dataType = JS_GetArrayBufferViewType(pixbuf.Obj()); michael@0: michael@0: // Check the pixels param type michael@0: if (dataType != requiredDataType) michael@0: return ErrorInvalidOperation("readPixels: Mismatched type/pixels types"); michael@0: michael@0: // Check the pixels param size michael@0: CheckedUint32 checked_neededByteLength = michael@0: GetImageSize(height, width, bytesPerPixel, mPixelStorePackAlignment); michael@0: michael@0: CheckedUint32 checked_plainRowSize = CheckedUint32(width) * bytesPerPixel; michael@0: michael@0: CheckedUint32 checked_alignedRowSize = michael@0: RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment); michael@0: michael@0: if (!checked_neededByteLength.isValid()) michael@0: return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size"); michael@0: michael@0: // Compute length and data. Don't reenter after this point, lest the michael@0: // precomputed go out of sync with the instant length/data. michael@0: pixbuf.ComputeLengthAndData(); michael@0: michael@0: uint32_t dataByteLen = pixbuf.Length(); michael@0: if (checked_neededByteLength.value() > dataByteLen) michael@0: return ErrorInvalidOperation("readPixels: buffer too small"); michael@0: michael@0: void* data = pixbuf.Data(); michael@0: if (!data) { michael@0: ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?"); michael@0: return rv.Throw(NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: bool isSourceTypeFloat = false; michael@0: if (mBoundFramebuffer && michael@0: mBoundFramebuffer->ColorAttachmentCount() && michael@0: mBoundFramebuffer->ColorAttachment(0).IsDefined()) michael@0: { michael@0: isSourceTypeFloat = mBoundFramebuffer->ColorAttachment(0).IsReadableFloat(); michael@0: } michael@0: michael@0: if (isReadTypeFloat != isSourceTypeFloat) michael@0: return ErrorInvalidOperation("readPixels: Invalid type floatness"); michael@0: michael@0: // Check the format and type params to assure they are an acceptable pair (as per spec) michael@0: switch (format) { michael@0: case LOCAL_GL_RGBA: { michael@0: switch (type) { michael@0: case LOCAL_GL_UNSIGNED_BYTE: michael@0: break; michael@0: case LOCAL_GL_FLOAT: michael@0: break; michael@0: default: michael@0: return ErrorInvalidOperation("readPixels: Invalid format/type pair"); michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: return ErrorInvalidOperation("readPixels: Invalid format/type pair"); michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: if (mBoundFramebuffer) { michael@0: // prevent readback of arbitrary video memory through uninitialized renderbuffers! michael@0: if (!mBoundFramebuffer->CheckAndInitializeAttachments()) michael@0: return ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer"); michael@0: michael@0: GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT; michael@0: if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) { michael@0: return ErrorInvalidOperation("readPixels: Read source attachment doesn't have the" michael@0: " correct color/depth/stencil type."); michael@0: } michael@0: } else { michael@0: ClearBackbufferIfNeeded(); michael@0: } michael@0: // Now that the errors are out of the way, on to actually reading michael@0: michael@0: // If we won't be reading any pixels anyways, just skip the actual reading michael@0: if (width == 0 || height == 0) michael@0: return DummyFramebufferOperation("readPixels"); michael@0: michael@0: if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) { michael@0: // the easy case: we're not reading out-of-range pixels michael@0: gl->fReadPixels(x, y, width, height, format, type, data); michael@0: } else { michael@0: // the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part michael@0: // of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL michael@0: // to do that for us, because passing out of range parameters to a buggy OpenGL implementation michael@0: // could conceivably allow to read memory we shouldn't be allowed to read. So we manually initialize michael@0: // the buffer to zero and compute the parameters to pass to OpenGL. We have to use an intermediate buffer michael@0: // to accomodate the potentially different strides (widths). michael@0: michael@0: // Zero the whole pixel dest area in the destination buffer. michael@0: memset(data, 0, checked_neededByteLength.value()); michael@0: michael@0: if ( x >= framebufferWidth michael@0: || x+width <= 0 michael@0: || y >= framebufferHeight michael@0: || y+height <= 0) michael@0: { michael@0: // we are completely outside of range, can exit now with buffer filled with zeros michael@0: return DummyFramebufferOperation("readPixels"); michael@0: } michael@0: michael@0: // compute the parameters of the subrect we're actually going to call glReadPixels on michael@0: GLint subrect_x = std::max(x, 0); michael@0: GLint subrect_end_x = std::min(x+width, framebufferWidth); michael@0: GLsizei subrect_width = subrect_end_x - subrect_x; michael@0: michael@0: GLint subrect_y = std::max(y, 0); michael@0: GLint subrect_end_y = std::min(y+height, framebufferHeight); michael@0: GLsizei subrect_height = subrect_end_y - subrect_y; michael@0: michael@0: if (subrect_width < 0 || subrect_height < 0 || michael@0: subrect_width > width || subrect_height > height) michael@0: return ErrorInvalidOperation("readPixels: integer overflow computing clipped rect size"); michael@0: michael@0: // now we know that subrect_width is in the [0..width] interval, and same for heights. michael@0: michael@0: // now, same computation as above to find the size of the intermediate buffer to allocate for the subrect michael@0: // no need to check again for integer overflow here, since we already know the sizes aren't greater than before michael@0: uint32_t subrect_plainRowSize = subrect_width * bytesPerPixel; michael@0: // There are checks above to ensure that this doesn't overflow. michael@0: uint32_t subrect_alignedRowSize = michael@0: RoundedToNextMultipleOf(subrect_plainRowSize, mPixelStorePackAlignment).value(); michael@0: uint32_t subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize; michael@0: michael@0: // create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer michael@0: GLubyte *subrect_data = new GLubyte[subrect_byteLength]; michael@0: gl->fReadPixels(subrect_x, subrect_y, subrect_width, subrect_height, format, type, subrect_data); michael@0: michael@0: // notice that this for loop terminates because we already checked that subrect_height is at most height michael@0: for (GLint y_inside_subrect = 0; y_inside_subrect < subrect_height; ++y_inside_subrect) { michael@0: GLint subrect_x_in_dest_buffer = subrect_x - x; michael@0: GLint subrect_y_in_dest_buffer = subrect_y - y; michael@0: memcpy(static_cast(data) michael@0: + checked_alignedRowSize.value() * (subrect_y_in_dest_buffer + y_inside_subrect) michael@0: + bytesPerPixel * subrect_x_in_dest_buffer, // destination michael@0: subrect_data + subrect_alignedRowSize * y_inside_subrect, // source michael@0: subrect_plainRowSize); // size michael@0: } michael@0: delete [] subrect_data; michael@0: } michael@0: michael@0: // if we're reading alpha, we may need to do fixup. Note that we don't allow michael@0: // GL_ALPHA to readpixels currently, but we had the code written for it already. michael@0: if (format == LOCAL_GL_ALPHA || michael@0: format == LOCAL_GL_RGBA) michael@0: { michael@0: bool needAlphaFixup; michael@0: if (mBoundFramebuffer) { michael@0: needAlphaFixup = !mBoundFramebuffer->ColorAttachment(0).HasAlpha(); michael@0: } else { michael@0: needAlphaFixup = gl->GetPixelFormat().alpha == 0; michael@0: } michael@0: michael@0: if (needAlphaFixup) { michael@0: if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) { michael@0: // this is easy; it's an 0xff memset per row michael@0: uint8_t *row = static_cast(data); michael@0: for (GLint j = 0; j < height; ++j) { michael@0: memset(row, 0xff, checked_plainRowSize.value()); michael@0: row += checked_alignedRowSize.value(); michael@0: } michael@0: } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) { michael@0: // this is harder, we need to just set the alpha byte here michael@0: uint8_t *row = static_cast(data); michael@0: for (GLint j = 0; j < height; ++j) { michael@0: uint8_t *rowp = row; michael@0: #if MOZ_LITTLE_ENDIAN michael@0: // offset to get the alpha byte; we're always going to michael@0: // move by 4 bytes michael@0: rowp += 3; michael@0: #endif michael@0: uint8_t *endrowp = rowp + 4 * width; michael@0: while (rowp != endrowp) { michael@0: *rowp = 0xff; michael@0: rowp += 4; michael@0: } michael@0: michael@0: row += checked_alignedRowSize.value(); michael@0: } michael@0: } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) { michael@0: float* row = static_cast(data); michael@0: michael@0: for (GLint j = 0; j < height; ++j) { michael@0: float* pAlpha = row + 3; michael@0: float* pAlphaEnd = pAlpha + 4*width; michael@0: michael@0: while (pAlpha != pAlphaEnd) { michael@0: *pAlpha = 1.0f; michael@0: pAlpha += 4; michael@0: } michael@0: michael@0: row += checked_alignedRowSize.value(); michael@0: } michael@0: } else { michael@0: NS_WARNING("Unhandled case, how'd we get here?"); michael@0: return rv.Throw(NS_ERROR_FAILURE); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: WebGLContext::RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!mBoundRenderbuffer) michael@0: return ErrorInvalidOperation("renderbufferStorage called on renderbuffer 0"); michael@0: michael@0: if (target != LOCAL_GL_RENDERBUFFER) michael@0: return ErrorInvalidEnumInfo("renderbufferStorage: target", target); michael@0: michael@0: if (width < 0 || height < 0) michael@0: return ErrorInvalidValue("renderbufferStorage: width and height must be >= 0"); michael@0: michael@0: if (width > mGLMaxRenderbufferSize || height > mGLMaxRenderbufferSize) michael@0: return ErrorInvalidValue("renderbufferStorage: width or height exceeds maximum renderbuffer size"); michael@0: michael@0: // certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL michael@0: GLenum internalformatForGL = internalformat; michael@0: michael@0: switch (internalformat) { michael@0: case LOCAL_GL_RGBA4: michael@0: case LOCAL_GL_RGB5_A1: michael@0: // 16-bit RGBA formats are not supported on desktop GL michael@0: if (!gl->IsGLES()) internalformatForGL = LOCAL_GL_RGBA8; michael@0: break; michael@0: case LOCAL_GL_RGB565: michael@0: // the RGB565 format is not supported on desktop GL michael@0: if (!gl->IsGLES()) internalformatForGL = LOCAL_GL_RGB8; michael@0: break; michael@0: case LOCAL_GL_DEPTH_COMPONENT16: michael@0: if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24)) michael@0: internalformatForGL = LOCAL_GL_DEPTH_COMPONENT24; michael@0: else if (gl->IsExtensionSupported(gl::GLContext::OES_packed_depth_stencil)) michael@0: internalformatForGL = LOCAL_GL_DEPTH24_STENCIL8; michael@0: break; michael@0: case LOCAL_GL_STENCIL_INDEX8: michael@0: break; michael@0: case LOCAL_GL_DEPTH_STENCIL: michael@0: // We emulate this in WebGLRenderbuffer if we don't have the requisite extension. michael@0: internalformatForGL = LOCAL_GL_DEPTH24_STENCIL8; michael@0: break; michael@0: case LOCAL_GL_SRGB8_ALPHA8_EXT: michael@0: break; michael@0: case LOCAL_GL_RGB16F: michael@0: case LOCAL_GL_RGBA16F: { michael@0: bool hasExtensions = IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float) && michael@0: IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float); michael@0: if (!hasExtensions) michael@0: return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", target); michael@0: break; michael@0: } michael@0: case LOCAL_GL_RGB32F: michael@0: case LOCAL_GL_RGBA32F: { michael@0: bool hasExtensions = IsExtensionEnabled(WebGLExtensionID::OES_texture_float) && michael@0: IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float); michael@0: if (!hasExtensions) michael@0: return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", target); michael@0: break; michael@0: } michael@0: default: michael@0: return ErrorInvalidEnumInfo("renderbufferStorage: internalformat", internalformat); michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: bool sizeChanges = width != mBoundRenderbuffer->Width() || michael@0: height != mBoundRenderbuffer->Height() || michael@0: internalformat != mBoundRenderbuffer->InternalFormat(); michael@0: if (sizeChanges) { michael@0: // Invalidate framebuffer status cache michael@0: mBoundRenderbuffer->NotifyFBsStatusChanged(); michael@0: GetAndFlushUnderlyingGLErrors(); michael@0: mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height); michael@0: GLenum error = GetAndFlushUnderlyingGLErrors(); michael@0: if (error) { michael@0: GenerateWarning("renderbufferStorage generated error %s", ErrorName(error)); michael@0: return; michael@0: } michael@0: } else { michael@0: mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height); michael@0: } michael@0: michael@0: mBoundRenderbuffer->SetInternalFormat(internalformat); michael@0: mBoundRenderbuffer->SetInternalFormatForGL(internalformatForGL); michael@0: mBoundRenderbuffer->setDimensions(width, height); michael@0: mBoundRenderbuffer->SetImageDataStatus(WebGLImageDataStatus::UninitializedImageData); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Scissor(GLint x, GLint y, GLsizei width, GLsizei height) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (width < 0 || height < 0) michael@0: return ErrorInvalidValue("scissor: negative size"); michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fScissor(x, y, width, height); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::StencilFunc(GLenum func, GLint ref, GLuint mask) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateComparisonEnum(func, "stencilFunc: func")) michael@0: return; michael@0: michael@0: mStencilRefFront = ref; michael@0: mStencilRefBack = ref; michael@0: mStencilValueMaskFront = mask; michael@0: mStencilValueMaskBack = mask; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fStencilFunc(func, ref, mask); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateFaceEnum(face, "stencilFuncSeparate: face") || michael@0: !ValidateComparisonEnum(func, "stencilFuncSeparate: func")) michael@0: return; michael@0: michael@0: switch (face) { michael@0: case LOCAL_GL_FRONT_AND_BACK: michael@0: mStencilRefFront = ref; michael@0: mStencilRefBack = ref; michael@0: mStencilValueMaskFront = mask; michael@0: mStencilValueMaskBack = mask; michael@0: break; michael@0: case LOCAL_GL_FRONT: michael@0: mStencilRefFront = ref; michael@0: mStencilValueMaskFront = mask; michael@0: break; michael@0: case LOCAL_GL_BACK: michael@0: mStencilRefBack = ref; michael@0: mStencilValueMaskBack = mask; michael@0: break; michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fStencilFuncSeparate(face, func, ref, mask); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateStencilOpEnum(sfail, "stencilOp: sfail") || michael@0: !ValidateStencilOpEnum(dpfail, "stencilOp: dpfail") || michael@0: !ValidateStencilOpEnum(dppass, "stencilOp: dppass")) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fStencilOp(sfail, dpfail, dppass); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateFaceEnum(face, "stencilOpSeparate: face") || michael@0: !ValidateStencilOpEnum(sfail, "stencilOpSeparate: sfail") || michael@0: !ValidateStencilOpEnum(dpfail, "stencilOpSeparate: dpfail") || michael@0: !ValidateStencilOpEnum(dppass, "stencilOpSeparate: dppass")) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fStencilOpSeparate(face, sfail, dpfail, dppass); michael@0: } michael@0: michael@0: nsresult michael@0: WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res, michael@0: RefPtr& imageOut, WebGLTexelFormat *format) michael@0: { michael@0: *format = WebGLTexelFormat::None; michael@0: michael@0: if (!res.mSourceSurface) michael@0: return NS_OK; michael@0: RefPtr data = res.mSourceSurface->GetDataSurface(); michael@0: if (!data) { michael@0: // SurfaceFromElement lied! michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mPixelStorePremultiplyAlpha && res.mIsPremultiplied) { michael@0: data = gfxUtils::UnpremultiplyDataSurface(data); michael@0: } michael@0: michael@0: // We disallow loading cross-domain images and videos that have not been validated michael@0: // with CORS as WebGL textures. The reason for doing that is that timing michael@0: // attacks on WebGL shaders are able to retrieve approximations of the michael@0: // pixel values in WebGL textures; see bug 655987. michael@0: // michael@0: // To prevent a loophole where a Canvas2D would be used as a proxy to load michael@0: // cross-domain textures, we also disallow loading textures from write-only michael@0: // Canvas2D's. michael@0: michael@0: // part 1: check that the DOM element is same-origin, or has otherwise been michael@0: // validated for cross-domain use. michael@0: if (!res.mCORSUsed) { michael@0: bool subsumes; michael@0: nsresult rv = mCanvasElement->NodePrincipal()->Subsumes(res.mPrincipal, &subsumes); michael@0: if (NS_FAILED(rv) || !subsumes) { michael@0: GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. " michael@0: "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures"); michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: } michael@0: michael@0: // part 2: if the DOM element is write-only, it might contain michael@0: // cross-domain image data. michael@0: if (res.mIsWriteOnly) { michael@0: GenerateWarning("The canvas used as source for texImage2D here is tainted (write-only). It is forbidden " michael@0: "to load a WebGL texture from a tainted canvas. A Canvas becomes tainted for example " michael@0: "when a cross-domain image is drawn on it. " michael@0: "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures"); michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: michael@0: // End of security checks, now we should be safe regarding cross-domain images michael@0: // Notice that there is never a need to mark the WebGL canvas as write-only, since we reject write-only/cross-domain michael@0: // texture sources in the first place. michael@0: michael@0: switch (data->GetFormat()) { michael@0: case SurfaceFormat::B8G8R8A8: michael@0: *format = WebGLTexelFormat::BGRA8; // careful, our ARGB means BGRA michael@0: break; michael@0: case SurfaceFormat::B8G8R8X8: michael@0: *format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8. michael@0: break; michael@0: case SurfaceFormat::A8: michael@0: *format = WebGLTexelFormat::A8; michael@0: break; michael@0: case SurfaceFormat::R5G6B5: michael@0: *format = WebGLTexelFormat::RGB565; michael@0: break; michael@0: default: michael@0: NS_ASSERTION(false, "Unsupported image format. Unimplemented."); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: imageOut = data; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: michael@0: void michael@0: WebGLContext::Uniform1i(WebGLUniformLocation *location_object, GLint a1) michael@0: { michael@0: GLint location; michael@0: if (!ValidateUniformSetter("Uniform1i", location_object, location)) michael@0: return; michael@0: michael@0: // Only uniform1i can take sampler settings. michael@0: if (!ValidateSamplerUniformSetter("Uniform1i", location_object, a1)) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fUniform1i(location, a1); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform2i(WebGLUniformLocation *location_object, GLint a1, michael@0: GLint a2) michael@0: { michael@0: GLint location; michael@0: if (!ValidateUniformSetter("Uniform2i", location_object, location)) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fUniform2i(location, a1, a2); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform3i(WebGLUniformLocation *location_object, GLint a1, michael@0: GLint a2, GLint a3) michael@0: { michael@0: GLint location; michael@0: if (!ValidateUniformSetter("Uniform3i", location_object, location)) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fUniform3i(location, a1, a2, a3); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform4i(WebGLUniformLocation *location_object, GLint a1, michael@0: GLint a2, GLint a3, GLint a4) michael@0: { michael@0: GLint location; michael@0: if (!ValidateUniformSetter("Uniform4i", location_object, location)) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fUniform4i(location, a1, a2, a3, a4); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform1f(WebGLUniformLocation *location_object, GLfloat a1) michael@0: { michael@0: GLint location; michael@0: if (!ValidateUniformSetter("Uniform1f", location_object, location)) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fUniform1f(location, a1); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform2f(WebGLUniformLocation *location_object, GLfloat a1, michael@0: GLfloat a2) michael@0: { michael@0: GLint location; michael@0: if (!ValidateUniformSetter("Uniform2f", location_object, location)) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fUniform2f(location, a1, a2); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform3f(WebGLUniformLocation *location_object, GLfloat a1, michael@0: GLfloat a2, GLfloat a3) michael@0: { michael@0: GLint location; michael@0: if (!ValidateUniformSetter("Uniform3f", location_object, location)) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fUniform3f(location, a1, a2, a3); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform4f(WebGLUniformLocation *location_object, GLfloat a1, michael@0: GLfloat a2, GLfloat a3, GLfloat a4) michael@0: { michael@0: GLint location; michael@0: if (!ValidateUniformSetter("Uniform4f", location_object, location)) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fUniform4f(location, a1, a2, a3, a4); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform1iv_base(WebGLUniformLocation *location_object, michael@0: uint32_t arrayLength, const GLint* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformArraySetter("Uniform1iv", 1, location_object, location, michael@0: numElementsToUpload, arrayLength)) { michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateSamplerUniformSetter("Uniform1iv", location_object, data[0])) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fUniform1iv(location, numElementsToUpload, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform2iv_base(WebGLUniformLocation *location_object, michael@0: uint32_t arrayLength, const GLint* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformArraySetter("Uniform2iv", 2, location_object, location, michael@0: numElementsToUpload, arrayLength)) { michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateSamplerUniformSetter("Uniform2iv", location_object, data[0]) || michael@0: !ValidateSamplerUniformSetter("Uniform2iv", location_object, data[1])) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fUniform2iv(location, numElementsToUpload, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform3iv_base(WebGLUniformLocation *location_object, michael@0: uint32_t arrayLength, const GLint* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformArraySetter("Uniform3iv", 3, location_object, location, michael@0: numElementsToUpload, arrayLength)) { michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateSamplerUniformSetter("Uniform3iv", location_object, data[0]) || michael@0: !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[1]) || michael@0: !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[2])) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fUniform3iv(location, numElementsToUpload, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform4iv_base(WebGLUniformLocation *location_object, michael@0: uint32_t arrayLength, const GLint* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformArraySetter("Uniform4iv", 4, location_object, location, michael@0: numElementsToUpload, arrayLength)) { michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateSamplerUniformSetter("Uniform4iv", location_object, data[0]) || michael@0: !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[1]) || michael@0: !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[2]) || michael@0: !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[3])) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fUniform4iv(location, numElementsToUpload, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform1fv_base(WebGLUniformLocation *location_object, michael@0: uint32_t arrayLength, const GLfloat* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformArraySetter("Uniform1fv", 1, location_object, location, michael@0: numElementsToUpload, arrayLength)) { michael@0: return; michael@0: } michael@0: MakeContextCurrent(); michael@0: gl->fUniform1fv(location, numElementsToUpload, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform2fv_base(WebGLUniformLocation *location_object, michael@0: uint32_t arrayLength, const GLfloat* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformArraySetter("Uniform2fv", 2, location_object, location, michael@0: numElementsToUpload, arrayLength)) { michael@0: return; michael@0: } michael@0: MakeContextCurrent(); michael@0: gl->fUniform2fv(location, numElementsToUpload, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform3fv_base(WebGLUniformLocation *location_object, michael@0: uint32_t arrayLength, const GLfloat* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformArraySetter("Uniform3fv", 3, location_object, location, michael@0: numElementsToUpload, arrayLength)) { michael@0: return; michael@0: } michael@0: MakeContextCurrent(); michael@0: gl->fUniform3fv(location, numElementsToUpload, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Uniform4fv_base(WebGLUniformLocation *location_object, michael@0: uint32_t arrayLength, const GLfloat* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformArraySetter("Uniform4fv", 4, location_object, location, michael@0: numElementsToUpload, arrayLength)) { michael@0: return; michael@0: } michael@0: MakeContextCurrent(); michael@0: gl->fUniform4fv(location, numElementsToUpload, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* location_object, michael@0: WebGLboolean aTranspose, uint32_t arrayLength, michael@0: const float* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformMatrixArraySetter("UniformMatrix2fv", 2, location_object, location, michael@0: numElementsToUpload, arrayLength, aTranspose)) { michael@0: return; michael@0: } michael@0: MakeContextCurrent(); michael@0: gl->fUniformMatrix2fv(location, numElementsToUpload, false, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* location_object, michael@0: WebGLboolean aTranspose, uint32_t arrayLength, michael@0: const float* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformMatrixArraySetter("UniformMatrix3fv", 3, location_object, location, michael@0: numElementsToUpload, arrayLength, aTranspose)) { michael@0: return; michael@0: } michael@0: MakeContextCurrent(); michael@0: gl->fUniformMatrix3fv(location, numElementsToUpload, false, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* location_object, michael@0: WebGLboolean aTranspose, uint32_t arrayLength, michael@0: const float* data) michael@0: { michael@0: uint32_t numElementsToUpload; michael@0: GLint location; michael@0: if (!ValidateUniformMatrixArraySetter("UniformMatrix4fv", 4, location_object, location, michael@0: numElementsToUpload, arrayLength, aTranspose)) { michael@0: return; michael@0: } michael@0: MakeContextCurrent(); michael@0: gl->fUniformMatrix4fv(location, numElementsToUpload, false, data); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::UseProgram(WebGLProgram *prog) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObjectAllowNull("useProgram", prog)) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: InvalidateBufferFetching(); michael@0: michael@0: GLuint progname = prog ? prog->GLName() : 0; michael@0: michael@0: if (prog && !prog->LinkStatus()) michael@0: return ErrorInvalidOperation("useProgram: program was not linked successfully"); michael@0: michael@0: gl->fUseProgram(progname); michael@0: michael@0: mCurrentProgram = prog; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ValidateProgram(WebGLProgram *prog) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObject("validateProgram", prog)) michael@0: return; michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: #ifdef XP_MACOSX michael@0: // see bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed with Mac OS 10.6.7 michael@0: if (gl->WorkAroundDriverBugs()) { michael@0: GenerateWarning("validateProgram: implemented as a no-operation on Mac to work around crashes"); michael@0: return; michael@0: } michael@0: #endif michael@0: michael@0: GLuint progname = prog->GLName(); michael@0: gl->fValidateProgram(progname); michael@0: } michael@0: michael@0: already_AddRefed michael@0: WebGLContext::CreateFramebuffer() michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: nsRefPtr globj = new WebGLFramebuffer(this); michael@0: return globj.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: WebGLContext::CreateRenderbuffer() michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: nsRefPtr globj = new WebGLRenderbuffer(this); michael@0: return globj.forget(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Viewport(GLint x, GLint y, GLsizei width, GLsizei height) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (width < 0 || height < 0) michael@0: return ErrorInvalidValue("viewport: negative size"); michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fViewport(x, y, width, height); michael@0: michael@0: mViewportX = x; michael@0: mViewportY = y; michael@0: mViewportWidth = width; michael@0: mViewportHeight = height; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::CompileShader(WebGLShader *shader) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObject("compileShader", shader)) michael@0: return; michael@0: michael@0: GLuint shadername = shader->GLName(); michael@0: michael@0: shader->SetCompileStatus(false); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: ShShaderOutput targetShaderSourceLanguage = gl->IsGLES() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT; michael@0: bool useShaderSourceTranslation = true; michael@0: michael@0: if (shader->NeedsTranslation() && mShaderValidation) { michael@0: ShHandle compiler = 0; michael@0: ShBuiltInResources resources; michael@0: memset(&resources, 0, sizeof(ShBuiltInResources)); michael@0: michael@0: resources.MaxVertexAttribs = mGLMaxVertexAttribs; michael@0: resources.MaxVertexUniformVectors = mGLMaxVertexUniformVectors; michael@0: resources.MaxVaryingVectors = mGLMaxVaryingVectors; michael@0: resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits; michael@0: resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits; michael@0: resources.MaxTextureImageUnits = mGLMaxTextureImageUnits; michael@0: resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors; michael@0: resources.MaxDrawBuffers = mGLMaxDrawBuffers; michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth)) michael@0: resources.EXT_frag_depth = 1; michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) michael@0: resources.OES_standard_derivatives = 1; michael@0: michael@0: if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) michael@0: resources.EXT_draw_buffers = 1; michael@0: michael@0: // Tell ANGLE to allow highp in frag shaders. (unless disabled) michael@0: // If underlying GLES doesn't have highp in frag shaders, it should complain anyways. michael@0: resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1; michael@0: michael@0: if (gl->WorkAroundDriverBugs()) { michael@0: #ifdef XP_MACOSX michael@0: if (gl->Vendor() == gl::GLVendor::NVIDIA) { michael@0: // Work around bug 890432 michael@0: resources.MaxExpressionComplexity = 1000; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: // We're storing an actual instance of StripComments because, if we don't, the michael@0: // cleanSource nsAString instance will be destroyed before the reference is michael@0: // actually used. michael@0: StripComments stripComments(shader->Source()); michael@0: const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length()); michael@0: if (!ValidateGLSLString(cleanSource, "compileShader")) michael@0: return; michael@0: michael@0: // shaderSource() already checks that the source stripped of comments is in the michael@0: // 7-bit ASCII range, so we can skip the NS_IsAscii() check. michael@0: NS_LossyConvertUTF16toASCII sourceCString(cleanSource); michael@0: michael@0: if (gl->WorkAroundDriverBugs()) { michael@0: const uint32_t maxSourceLength = 0x3ffff; michael@0: if (sourceCString.Length() > maxSourceLength) michael@0: return ErrorInvalidValue("compileShader: source has more than %d characters", michael@0: maxSourceLength); michael@0: } michael@0: michael@0: const char *s = sourceCString.get(); michael@0: michael@0: #define WEBGL2_BYPASS_ANGLE michael@0: #ifdef WEBGL2_BYPASS_ANGLE michael@0: /* michael@0: * The bypass don't bring a full support for GLSL ES 3.0, but the main purpose michael@0: * is to natively bring gl_InstanceID (to do instanced rendering) and gl_FragData michael@0: * michael@0: * To remove the bypass code, just comment #define WEBGL2_BYPASS_ANGLE above michael@0: * michael@0: * To bypass angle, the context must be a WebGL 2 and the shader must have the michael@0: * following line at the very top : michael@0: * #version proto-200 michael@0: * michael@0: * In this case, byPassANGLE == true and here is what we do : michael@0: * We create two shader source code: michael@0: * - one for the driver, that enable GL_EXT_gpu_shader4 michael@0: * - one for the angle compilor, to get informations about vertex attributes michael@0: * and uniforms michael@0: */ michael@0: static const char *bypassPrefixSearch = "#version proto-200"; michael@0: static const char *bypassANGLEPrefix[2] = {"precision mediump float;\n" michael@0: "#define gl_VertexID 0\n" michael@0: "#define gl_InstanceID 0\n", michael@0: michael@0: "precision mediump float;\n" michael@0: "#extension GL_EXT_draw_buffers : enable\n" michael@0: "#define gl_PrimitiveID 0\n"}; michael@0: michael@0: const bool bypassANGLE = IsWebGL2() && (strstr(s, bypassPrefixSearch) != 0); michael@0: michael@0: const char *angleShaderCode = s; michael@0: nsTArray bypassANGLEShaderCode; michael@0: nsTArray bypassDriverShaderCode; michael@0: michael@0: if (bypassANGLE) { michael@0: const int bypassStage = (shader->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) ? 1 : 0; michael@0: const char *originalShader = strstr(s, bypassPrefixSearch) + strlen(bypassPrefixSearch); michael@0: int originalShaderSize = strlen(s) - (originalShader - s); michael@0: int bypassShaderCodeSize = originalShaderSize + 4096 + 1; michael@0: michael@0: bypassANGLEShaderCode.SetLength(bypassShaderCodeSize); michael@0: strcpy(bypassANGLEShaderCode.Elements(), bypassANGLEPrefix[bypassStage]); michael@0: strcat(bypassANGLEShaderCode.Elements(), originalShader); michael@0: michael@0: bypassDriverShaderCode.SetLength(bypassShaderCodeSize); michael@0: strcpy(bypassDriverShaderCode.Elements(), "#extension GL_EXT_gpu_shader4 : enable\n"); michael@0: strcat(bypassDriverShaderCode.Elements(), originalShader); michael@0: michael@0: angleShaderCode = bypassANGLEShaderCode.Elements(); michael@0: } michael@0: #endif michael@0: michael@0: compiler = ShConstructCompiler((ShShaderType) shader->ShaderType(), michael@0: SH_WEBGL_SPEC, michael@0: targetShaderSourceLanguage, michael@0: &resources); michael@0: michael@0: int compileOptions = SH_ATTRIBUTES_UNIFORMS | michael@0: SH_ENFORCE_PACKING_RESTRICTIONS; michael@0: michael@0: if (resources.MaxExpressionComplexity > 0) { michael@0: compileOptions |= SH_LIMIT_EXPRESSION_COMPLEXITY; michael@0: } michael@0: michael@0: // We want to do this everywhere, but: michael@0: #ifndef XP_MACOSX // To do this on Mac, we need to do it only on Mac OSX > 10.6 as this michael@0: // causes the shader compiler in 10.6 to crash michael@0: compileOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS; michael@0: #endif michael@0: michael@0: if (useShaderSourceTranslation) { michael@0: compileOptions |= SH_OBJECT_CODE michael@0: | SH_MAP_LONG_VARIABLE_NAMES; michael@0: michael@0: #ifdef XP_MACOSX michael@0: if (gl->WorkAroundDriverBugs()) { michael@0: // Work around bug 665578 and bug 769810 michael@0: if (gl->Vendor() == gl::GLVendor::ATI) { michael@0: compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; michael@0: } michael@0: michael@0: // Work around bug 735560 michael@0: if (gl->Vendor() == gl::GLVendor::Intel) { michael@0: compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #ifdef WEBGL2_BYPASS_ANGLE michael@0: if (!ShCompile(compiler, &angleShaderCode, 1, compileOptions)) { michael@0: #else michael@0: if (!ShCompile(compiler, &s, 1, compileOptions)) { michael@0: #endif michael@0: size_t lenWithNull = 0; michael@0: ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &lenWithNull); michael@0: michael@0: if (!lenWithNull) { michael@0: // Error in ShGetInfo. michael@0: shader->SetTranslationFailure(NS_LITERAL_CSTRING("Internal error: failed to get shader info log")); michael@0: } else { michael@0: size_t len = lenWithNull - 1; michael@0: michael@0: nsAutoCString info; michael@0: info.SetLength(len); // Allocates len+1, for the null-term. michael@0: ShGetInfoLog(compiler, info.BeginWriting()); michael@0: michael@0: shader->SetTranslationFailure(info); michael@0: } michael@0: ShDestruct(compiler); michael@0: shader->SetCompileStatus(false); michael@0: return; michael@0: } michael@0: michael@0: size_t num_attributes = 0; michael@0: ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTES, &num_attributes); michael@0: size_t num_uniforms = 0; michael@0: ShGetInfo(compiler, SH_ACTIVE_UNIFORMS, &num_uniforms); michael@0: size_t attrib_max_length = 0; michael@0: ShGetInfo(compiler, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attrib_max_length); michael@0: size_t uniform_max_length = 0; michael@0: ShGetInfo(compiler, SH_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_length); michael@0: size_t mapped_max_length = 0; michael@0: ShGetInfo(compiler, SH_MAPPED_NAME_MAX_LENGTH, &mapped_max_length); michael@0: michael@0: shader->mAttribMaxNameLength = attrib_max_length; michael@0: michael@0: shader->mAttributes.Clear(); michael@0: shader->mUniforms.Clear(); michael@0: shader->mUniformInfos.Clear(); michael@0: michael@0: nsAutoArrayPtr attribute_name(new char[attrib_max_length+1]); michael@0: nsAutoArrayPtr uniform_name(new char[uniform_max_length+1]); michael@0: nsAutoArrayPtr mapped_name(new char[mapped_max_length+1]); michael@0: michael@0: for (size_t i = 0; i < num_uniforms; i++) { michael@0: size_t length; michael@0: int size; michael@0: ShDataType type; michael@0: ShGetActiveUniform(compiler, (int)i, michael@0: &length, &size, &type, michael@0: uniform_name, michael@0: mapped_name); michael@0: if (useShaderSourceTranslation) { michael@0: shader->mUniforms.AppendElement(WebGLMappedIdentifier( michael@0: nsDependentCString(uniform_name), michael@0: nsDependentCString(mapped_name))); michael@0: } michael@0: michael@0: // we always query uniform info, regardless of useShaderSourceTranslation, michael@0: // as we need it to validate uniform setter calls, and it doesn't rely on michael@0: // shader translation. michael@0: char mappedNameLength = strlen(mapped_name); michael@0: char mappedNameLastChar = mappedNameLength > 1 michael@0: ? mapped_name[mappedNameLength - 1] michael@0: : 0; michael@0: shader->mUniformInfos.AppendElement(WebGLUniformInfo( michael@0: size, michael@0: mappedNameLastChar == ']', michael@0: type)); michael@0: } michael@0: michael@0: if (useShaderSourceTranslation) { michael@0: michael@0: for (size_t i = 0; i < num_attributes; i++) { michael@0: size_t length; michael@0: int size; michael@0: ShDataType type; michael@0: ShGetActiveAttrib(compiler, (int)i, michael@0: &length, &size, &type, michael@0: attribute_name, michael@0: mapped_name); michael@0: shader->mAttributes.AppendElement(WebGLMappedIdentifier( michael@0: nsDependentCString(attribute_name), michael@0: nsDependentCString(mapped_name))); michael@0: } michael@0: michael@0: size_t lenWithNull = 0; michael@0: ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &lenWithNull); michael@0: MOZ_ASSERT(lenWithNull >= 1); michael@0: size_t len = lenWithNull - 1; michael@0: michael@0: nsAutoCString translatedSrc; michael@0: translatedSrc.SetLength(len); // Allocates len+1, for the null-term. michael@0: ShGetObjectCode(compiler, translatedSrc.BeginWriting()); michael@0: michael@0: CopyASCIItoUTF16(translatedSrc, shader->mTranslatedSource); michael@0: michael@0: const char *ts = translatedSrc.get(); michael@0: michael@0: #ifdef WEBGL2_BYPASS_ANGLE michael@0: if (bypassANGLE) { michael@0: const char* driverShaderCode = bypassDriverShaderCode.Elements(); michael@0: gl->fShaderSource(shadername, 1, (const GLchar**) &driverShaderCode, nullptr); michael@0: } michael@0: else { michael@0: gl->fShaderSource(shadername, 1, &ts, nullptr); michael@0: } michael@0: #else michael@0: gl->fShaderSource(shadername, 1, &ts, nullptr); michael@0: #endif michael@0: } else { // not useShaderSourceTranslation michael@0: // we just pass the raw untranslated shader source. We then can't use ANGLE idenfier mapping. michael@0: // that's really bad, as that means we can't be 100% conformant. We should work towards always michael@0: // using ANGLE identifier mapping. michael@0: gl->fShaderSource(shadername, 1, &s, nullptr); michael@0: michael@0: CopyASCIItoUTF16(s, shader->mTranslatedSource); michael@0: } michael@0: michael@0: shader->SetTranslationSuccess(); michael@0: michael@0: ShDestruct(compiler); michael@0: michael@0: gl->fCompileShader(shadername); michael@0: GLint ok; michael@0: gl->fGetShaderiv(shadername, LOCAL_GL_COMPILE_STATUS, &ok); michael@0: shader->SetCompileStatus(ok); michael@0: } michael@0: } michael@0: michael@0: void michael@0: WebGLContext::CompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, michael@0: GLsizei width, GLsizei height, GLint border, michael@0: const ArrayBufferView& view) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage; michael@0: michael@0: if (!ValidateTexImage(2, target, level, internalformat, michael@0: 0, 0, 0, width, height, 0, michael@0: border, internalformat, LOCAL_GL_UNSIGNED_BYTE, michael@0: func)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: view.ComputeLengthAndData(); michael@0: michael@0: uint32_t byteLength = view.Length(); michael@0: if (!ValidateCompTexImageDataSize(target, internalformat, width, height, byteLength, func)) { michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateCompTexImageSize(target, level, internalformat, 0, 0, michael@0: width, height, width, height, func)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fCompressedTexImage2D(target, level, internalformat, width, height, border, byteLength, view.Data()); michael@0: WebGLTexture* tex = activeBoundTextureForTarget(target); michael@0: MOZ_ASSERT(tex); michael@0: tex->SetImageInfo(target, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE, michael@0: WebGLImageDataStatus::InitializedImageData); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::CompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, michael@0: GLint yoffset, GLsizei width, GLsizei height, michael@0: GLenum format, const ArrayBufferView& view) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage; michael@0: michael@0: if (!ValidateTexImage(2, target, michael@0: level, format, michael@0: xoffset, yoffset, 0, michael@0: width, height, 0, michael@0: 0, format, LOCAL_GL_UNSIGNED_BYTE, michael@0: func)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: WebGLTexture *tex = activeBoundTextureForTarget(target); michael@0: MOZ_ASSERT(tex); michael@0: WebGLTexture::ImageInfo& levelInfo = tex->ImageInfoAt(target, level); michael@0: michael@0: view.ComputeLengthAndData(); michael@0: michael@0: uint32_t byteLength = view.Length(); michael@0: if (!ValidateCompTexImageDataSize(target, format, width, height, byteLength, func)) michael@0: return; michael@0: michael@0: if (!ValidateCompTexImageSize(target, level, format, michael@0: xoffset, yoffset, michael@0: width, height, michael@0: levelInfo.Width(), levelInfo.Height(), michael@0: func)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: if (levelInfo.HasUninitializedImageData()) michael@0: tex->DoDeferredImageInitialization(target, level); michael@0: michael@0: MakeContextCurrent(); michael@0: gl->fCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, byteLength, view.Data()); michael@0: } michael@0: michael@0: JS::Value michael@0: WebGLContext::GetShaderParameter(WebGLShader *shader, GLenum pname) michael@0: { michael@0: if (IsContextLost()) michael@0: return JS::NullValue(); michael@0: michael@0: if (!ValidateObject("getShaderParameter: shader", shader)) michael@0: return JS::NullValue(); michael@0: michael@0: GLuint shadername = shader->GLName(); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: switch (pname) { michael@0: case LOCAL_GL_SHADER_TYPE: michael@0: { michael@0: GLint i = 0; michael@0: gl->fGetShaderiv(shadername, pname, &i); michael@0: return JS::NumberValue(uint32_t(i)); michael@0: } michael@0: break; michael@0: case LOCAL_GL_DELETE_STATUS: michael@0: return JS::BooleanValue(shader->IsDeleteRequested()); michael@0: break; michael@0: case LOCAL_GL_COMPILE_STATUS: michael@0: { michael@0: GLint i = 0; michael@0: gl->fGetShaderiv(shadername, pname, &i); michael@0: return JS::BooleanValue(bool(i)); michael@0: } michael@0: break; michael@0: default: michael@0: ErrorInvalidEnumInfo("getShaderParameter: parameter", pname); michael@0: } michael@0: michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetShaderInfoLog(WebGLShader *shader, nsAString& retval) michael@0: { michael@0: nsAutoCString s; michael@0: GetShaderInfoLog(shader, s); michael@0: if (s.IsVoid()) michael@0: retval.SetIsVoid(true); michael@0: else michael@0: CopyASCIItoUTF16(s, retval); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetShaderInfoLog(WebGLShader *shader, nsACString& retval) michael@0: { michael@0: if (IsContextLost()) michael@0: { michael@0: retval.SetIsVoid(true); michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateObject("getShaderInfoLog: shader", shader)) michael@0: return; michael@0: michael@0: retval = shader->TranslationLog(); michael@0: if (!retval.IsVoid()) { michael@0: return; michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: GLuint shadername = shader->GLName(); michael@0: GLint k = -1; michael@0: gl->fGetShaderiv(shadername, LOCAL_GL_INFO_LOG_LENGTH, &k); michael@0: if (k == -1) { michael@0: // XXX GL Error? should never happen. michael@0: return; michael@0: } michael@0: michael@0: if (k == 0) { michael@0: retval.Truncate(); michael@0: return; michael@0: } michael@0: michael@0: retval.SetCapacity(k); michael@0: gl->fGetShaderInfoLog(shadername, k, &k, (char*) retval.BeginWriting()); michael@0: retval.SetLength(k); michael@0: } michael@0: michael@0: already_AddRefed michael@0: WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype) michael@0: { michael@0: if (IsContextLost()) michael@0: return nullptr; michael@0: michael@0: switch (shadertype) { michael@0: case LOCAL_GL_FRAGMENT_SHADER: michael@0: case LOCAL_GL_VERTEX_SHADER: michael@0: break; michael@0: default: michael@0: ErrorInvalidEnumInfo("getShaderPrecisionFormat: shadertype", shadertype); michael@0: return nullptr; michael@0: } michael@0: michael@0: switch (precisiontype) { michael@0: case LOCAL_GL_LOW_FLOAT: michael@0: case LOCAL_GL_MEDIUM_FLOAT: michael@0: case LOCAL_GL_HIGH_FLOAT: michael@0: case LOCAL_GL_LOW_INT: michael@0: case LOCAL_GL_MEDIUM_INT: michael@0: case LOCAL_GL_HIGH_INT: michael@0: break; michael@0: default: michael@0: ErrorInvalidEnumInfo("getShaderPrecisionFormat: precisiontype", precisiontype); michael@0: return nullptr; michael@0: } michael@0: michael@0: MakeContextCurrent(); michael@0: GLint range[2], precision; michael@0: michael@0: if (mDisableFragHighP && michael@0: shadertype == LOCAL_GL_FRAGMENT_SHADER && michael@0: (precisiontype == LOCAL_GL_HIGH_FLOAT || michael@0: precisiontype == LOCAL_GL_HIGH_INT)) michael@0: { michael@0: precision = 0; michael@0: range[0] = 0; michael@0: range[1] = 0; michael@0: } else { michael@0: gl->fGetShaderPrecisionFormat(shadertype, precisiontype, range, &precision); michael@0: } michael@0: michael@0: nsRefPtr retShaderPrecisionFormat michael@0: = new WebGLShaderPrecisionFormat(this, range[0], range[1], precision); michael@0: return retShaderPrecisionFormat.forget(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetShaderSource(WebGLShader *shader, nsAString& retval) michael@0: { michael@0: if (IsContextLost()) { michael@0: retval.SetIsVoid(true); michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateObject("getShaderSource: shader", shader)) michael@0: return; michael@0: michael@0: retval.Assign(shader->Source()); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::ShaderSource(WebGLShader *shader, const nsAString& source) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!ValidateObject("shaderSource: shader", shader)) michael@0: return; michael@0: michael@0: // We're storing an actual instance of StripComments because, if we don't, the michael@0: // cleanSource nsAString instance will be destroyed before the reference is michael@0: // actually used. michael@0: StripComments stripComments(source); michael@0: const nsAString& cleanSource = Substring(stripComments.result().Elements(), stripComments.length()); michael@0: if (!ValidateGLSLString(cleanSource, "compileShader")) michael@0: return; michael@0: michael@0: shader->SetSource(source); michael@0: michael@0: shader->SetNeedsTranslation(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::GetShaderTranslatedSource(WebGLShader *shader, nsAString& retval) michael@0: { michael@0: if (IsContextLost()) { michael@0: retval.SetIsVoid(true); michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateObject("getShaderTranslatedSource: shader", shader)) michael@0: return; michael@0: michael@0: retval.Assign(shader->TranslatedSource()); michael@0: } michael@0: michael@0: GLenum WebGLContext::CheckedTexImage2D(GLenum target, michael@0: GLint level, michael@0: GLenum internalFormat, michael@0: GLsizei width, michael@0: GLsizei height, michael@0: GLint border, michael@0: GLenum format, michael@0: GLenum type, michael@0: const GLvoid *data) michael@0: { michael@0: MOZ_ASSERT(internalFormat == format); michael@0: WebGLTexture *tex = activeBoundTextureForTarget(target); michael@0: MOZ_ASSERT(tex != nullptr, "no texture bound"); michael@0: michael@0: bool sizeMayChange = true; michael@0: michael@0: if (tex->HasImageInfoAt(target, level)) { michael@0: const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(target, level); michael@0: sizeMayChange = width != imageInfo.Width() || michael@0: height != imageInfo.Height() || michael@0: format != imageInfo.WebGLFormat() || michael@0: type != imageInfo.WebGLType(); michael@0: } michael@0: michael@0: // Convert to format and type required by OpenGL 'driver'. michael@0: GLenum driverType = DriverTypeFromType(gl, type); michael@0: GLenum driverInternalFormat = LOCAL_GL_NONE; michael@0: GLenum driverFormat = LOCAL_GL_NONE; michael@0: DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat); michael@0: michael@0: if (sizeMayChange) { michael@0: GetAndFlushUnderlyingGLErrors(); michael@0: } michael@0: michael@0: gl->fTexImage2D(target, level, driverInternalFormat, width, height, border, driverFormat, driverType, data); michael@0: michael@0: GLenum error = LOCAL_GL_NO_ERROR; michael@0: if (sizeMayChange) { michael@0: error = GetAndFlushUnderlyingGLErrors(); michael@0: } michael@0: michael@0: return error; michael@0: } michael@0: michael@0: void michael@0: WebGLContext::TexImage2D_base(GLenum target, GLint level, GLenum internalformat, michael@0: GLsizei width, GLsizei height, GLsizei srcStrideOrZero, michael@0: GLint border, michael@0: GLenum format, GLenum type, michael@0: void* data, uint32_t byteLength, michael@0: int jsArrayType, // a TypedArray format enum, or -1 if not relevant michael@0: WebGLTexelFormat srcFormat, bool srcPremultiplied) michael@0: { michael@0: const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage; michael@0: michael@0: if (!ValidateTexImage(2, target, level, internalformat, michael@0: 0, 0, 0, michael@0: width, height, 0, michael@0: border, format, type, func)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: const bool isDepthTexture = format == LOCAL_GL_DEPTH_COMPONENT || michael@0: format == LOCAL_GL_DEPTH_STENCIL; michael@0: michael@0: if (isDepthTexture) { michael@0: if (data != nullptr || level != 0) michael@0: return ErrorInvalidOperation("texImage2D: " michael@0: "with format of DEPTH_COMPONENT or DEPTH_STENCIL, " michael@0: "data must be nullptr, " michael@0: "level must be zero"); michael@0: } michael@0: michael@0: if (!ValidateTexInputData(type, jsArrayType, func)) michael@0: return; michael@0: michael@0: WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type); michael@0: WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat; michael@0: michael@0: uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat); michael@0: michael@0: CheckedUint32 checked_neededByteLength = michael@0: GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment); michael@0: michael@0: CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize; michael@0: CheckedUint32 checked_alignedRowSize = michael@0: RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment); michael@0: michael@0: if (!checked_neededByteLength.isValid()) michael@0: return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size"); michael@0: michael@0: uint32_t bytesNeeded = checked_neededByteLength.value(); michael@0: michael@0: if (byteLength && byteLength < bytesNeeded) michael@0: return ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)", michael@0: bytesNeeded, byteLength); michael@0: michael@0: WebGLTexture *tex = activeBoundTextureForTarget(target); michael@0: michael@0: if (!tex) michael@0: return ErrorInvalidOperation("texImage2D: no texture is bound to this target"); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: nsAutoArrayPtr convertedData; michael@0: void* pixels = nullptr; michael@0: WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData; michael@0: michael@0: if (byteLength) { michael@0: size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value(); michael@0: uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8; michael@0: size_t dstPlainRowSize = dstTexelSize * width; michael@0: size_t unpackAlignment = mPixelStoreUnpackAlignment; michael@0: size_t dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment; michael@0: michael@0: if (actualSrcFormat == dstFormat && michael@0: srcPremultiplied == mPixelStorePremultiplyAlpha && michael@0: srcStride == dstStride && michael@0: !mPixelStoreFlipY) michael@0: { michael@0: // no conversion, no flipping, so we avoid copying anything and just pass the source pointer michael@0: pixels = data; michael@0: } michael@0: else michael@0: { michael@0: size_t convertedDataSize = height * dstStride; michael@0: convertedData = new uint8_t[convertedDataSize]; michael@0: ConvertImage(width, height, srcStride, dstStride, michael@0: static_cast(data), convertedData, michael@0: actualSrcFormat, srcPremultiplied, michael@0: dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize); michael@0: pixels = reinterpret_cast(convertedData.get()); michael@0: } michael@0: imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData; michael@0: } michael@0: michael@0: GLenum error = CheckedTexImage2D(target, level, internalformat, width, michael@0: height, border, format, type, pixels); michael@0: michael@0: if (error) { michael@0: GenerateWarning("texImage2D generated error %s", ErrorName(error)); michael@0: return; michael@0: } michael@0: michael@0: // in all of the code paths above, we should have either initialized data, michael@0: // or allocated data and left it uninitialized, but in any case we shouldn't michael@0: // have NoImageData at this point. michael@0: MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData); michael@0: michael@0: tex->SetImageInfo(target, level, width, height, format, type, imageInfoStatusIfSuccess); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::TexImage2D(GLenum target, GLint level, michael@0: GLenum internalformat, GLsizei width, michael@0: GLsizei height, GLint border, GLenum format, michael@0: GLenum type, const Nullable &pixels, ErrorResult& rv) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: void* data; michael@0: uint32_t length; michael@0: int jsArrayType; michael@0: if (pixels.IsNull()) { michael@0: data = nullptr; michael@0: length = 0; michael@0: jsArrayType = -1; michael@0: } else { michael@0: const ArrayBufferView& view = pixels.Value(); michael@0: view.ComputeLengthAndData(); michael@0: michael@0: data = view.Data(); michael@0: length = view.Length(); michael@0: jsArrayType = int(JS_GetArrayBufferViewType(view.Obj())); michael@0: } michael@0: michael@0: return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type, michael@0: data, length, jsArrayType, michael@0: WebGLTexelFormat::Auto, false); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::TexImage2D(GLenum target, GLint level, michael@0: GLenum internalformat, GLenum format, michael@0: GLenum type, ImageData* pixels, ErrorResult& rv) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!pixels) { michael@0: // Spec says to generate an INVALID_VALUE error michael@0: return ErrorInvalidValue("texImage2D: null ImageData"); michael@0: } michael@0: michael@0: Uint8ClampedArray arr(pixels->GetDataObject()); michael@0: arr.ComputeLengthAndData(); michael@0: michael@0: return TexImage2D_base(target, level, internalformat, pixels->Width(), michael@0: pixels->Height(), 4*pixels->Width(), 0, michael@0: format, type, arr.Data(), arr.Length(), -1, michael@0: WebGLTexelFormat::RGBA8, false); michael@0: } michael@0: michael@0: michael@0: void michael@0: WebGLContext::TexSubImage2D_base(GLenum target, GLint level, michael@0: GLint xoffset, GLint yoffset, michael@0: GLsizei width, GLsizei height, GLsizei srcStrideOrZero, michael@0: GLenum format, GLenum type, michael@0: void* data, uint32_t byteLength, michael@0: int jsArrayType, michael@0: WebGLTexelFormat srcFormat, bool srcPremultiplied) michael@0: { michael@0: const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage; michael@0: michael@0: if (!ValidateTexImage(2, target, level, format, michael@0: xoffset, yoffset, 0, michael@0: width, height, 0, michael@0: 0, format, type, func)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: if (!ValidateTexInputData(type, jsArrayType, func)) michael@0: return; michael@0: michael@0: WebGLTexelFormat dstFormat = GetWebGLTexelFormat(format, type); michael@0: WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat; michael@0: michael@0: uint32_t srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(actualSrcFormat); michael@0: michael@0: if (width == 0 || height == 0) michael@0: return; // ES 2.0 says it has no effect, we better return right now michael@0: michael@0: CheckedUint32 checked_neededByteLength = michael@0: GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment); michael@0: michael@0: CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize; michael@0: michael@0: CheckedUint32 checked_alignedRowSize = michael@0: RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment); michael@0: michael@0: if (!checked_neededByteLength.isValid()) michael@0: return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size"); michael@0: michael@0: uint32_t bytesNeeded = checked_neededByteLength.value(); michael@0: michael@0: if (byteLength < bytesNeeded) michael@0: return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength); michael@0: michael@0: WebGLTexture *tex = activeBoundTextureForTarget(target); michael@0: const WebGLTexture::ImageInfo &imageInfo = tex->ImageInfoAt(target, level); michael@0: michael@0: if (imageInfo.HasUninitializedImageData()) michael@0: tex->DoDeferredImageInitialization(target, level); michael@0: michael@0: MakeContextCurrent(); michael@0: michael@0: size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value(); michael@0: uint32_t dstTexelSize = GetBitsPerTexel(format, type) / 8; michael@0: size_t dstPlainRowSize = dstTexelSize * width; michael@0: // There are checks above to ensure that this won't overflow. michael@0: size_t dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mPixelStoreUnpackAlignment).value(); michael@0: michael@0: void* pixels = data; michael@0: nsAutoArrayPtr convertedData; michael@0: michael@0: // no conversion, no flipping, so we avoid copying anything and just pass the source pointer michael@0: bool noConversion = (actualSrcFormat == dstFormat && michael@0: srcPremultiplied == mPixelStorePremultiplyAlpha && michael@0: srcStride == dstStride && michael@0: !mPixelStoreFlipY); michael@0: michael@0: if (!noConversion) { michael@0: size_t convertedDataSize = height * dstStride; michael@0: convertedData = new uint8_t[convertedDataSize]; michael@0: ConvertImage(width, height, srcStride, dstStride, michael@0: static_cast(data), convertedData, michael@0: actualSrcFormat, srcPremultiplied, michael@0: dstFormat, mPixelStorePremultiplyAlpha, dstTexelSize); michael@0: pixels = reinterpret_cast(convertedData.get()); michael@0: } michael@0: michael@0: GLenum driverType = DriverTypeFromType(gl, type); michael@0: GLenum driverInternalFormat = LOCAL_GL_NONE; michael@0: GLenum driverFormat = LOCAL_GL_NONE; michael@0: DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat); michael@0: michael@0: gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, driverFormat, driverType, pixels); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::TexSubImage2D(GLenum target, GLint level, michael@0: GLint xoffset, GLint yoffset, michael@0: GLsizei width, GLsizei height, michael@0: GLenum format, GLenum type, michael@0: const Nullable &pixels, michael@0: ErrorResult& rv) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (pixels.IsNull()) michael@0: return ErrorInvalidValue("texSubImage2D: pixels must not be null!"); michael@0: michael@0: const ArrayBufferView& view = pixels.Value(); michael@0: view.ComputeLengthAndData(); michael@0: michael@0: return TexSubImage2D_base(target, level, xoffset, yoffset, michael@0: width, height, 0, format, type, michael@0: view.Data(), view.Length(), michael@0: JS_GetArrayBufferViewType(view.Obj()), michael@0: WebGLTexelFormat::Auto, false); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::TexSubImage2D(GLenum target, GLint level, michael@0: GLint xoffset, GLint yoffset, michael@0: GLenum format, GLenum type, ImageData* pixels, michael@0: ErrorResult& rv) michael@0: { michael@0: if (IsContextLost()) michael@0: return; michael@0: michael@0: if (!pixels) michael@0: return ErrorInvalidValue("texSubImage2D: pixels must not be null!"); michael@0: michael@0: Uint8ClampedArray arr(pixels->GetDataObject()); michael@0: arr.ComputeLengthAndData(); michael@0: michael@0: return TexSubImage2D_base(target, level, xoffset, yoffset, michael@0: pixels->Width(), pixels->Height(), michael@0: 4*pixels->Width(), format, type, michael@0: arr.Data(), arr.Length(), michael@0: -1, michael@0: WebGLTexelFormat::RGBA8, false); michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::LoseContext() michael@0: { michael@0: if (IsContextLost()) michael@0: return false; michael@0: michael@0: ForceLoseContext(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLContext::RestoreContext() michael@0: { michael@0: if (!IsContextLost() || !mAllowRestore) { michael@0: return false; michael@0: } michael@0: michael@0: ForceRestoreContext(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize) michael@0: { michael@0: switch (uType) { michael@0: case LOCAL_GL_INT: michael@0: case LOCAL_GL_INT_VEC2: michael@0: case LOCAL_GL_INT_VEC3: michael@0: case LOCAL_GL_INT_VEC4: michael@0: case LOCAL_GL_SAMPLER_2D: michael@0: case LOCAL_GL_SAMPLER_CUBE: michael@0: *baseType = LOCAL_GL_INT; michael@0: break; michael@0: case LOCAL_GL_FLOAT: michael@0: case LOCAL_GL_FLOAT_VEC2: michael@0: case LOCAL_GL_FLOAT_VEC3: michael@0: case LOCAL_GL_FLOAT_VEC4: michael@0: case LOCAL_GL_FLOAT_MAT2: michael@0: case LOCAL_GL_FLOAT_MAT3: michael@0: case LOCAL_GL_FLOAT_MAT4: michael@0: *baseType = LOCAL_GL_FLOAT; michael@0: break; michael@0: case LOCAL_GL_BOOL: michael@0: case LOCAL_GL_BOOL_VEC2: michael@0: case LOCAL_GL_BOOL_VEC3: michael@0: case LOCAL_GL_BOOL_VEC4: michael@0: *baseType = LOCAL_GL_BOOL; // pretend these are int michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: michael@0: switch (uType) { michael@0: case LOCAL_GL_INT: michael@0: case LOCAL_GL_FLOAT: michael@0: case LOCAL_GL_BOOL: michael@0: case LOCAL_GL_SAMPLER_2D: michael@0: case LOCAL_GL_SAMPLER_CUBE: michael@0: *unitSize = 1; michael@0: break; michael@0: case LOCAL_GL_INT_VEC2: michael@0: case LOCAL_GL_FLOAT_VEC2: michael@0: case LOCAL_GL_BOOL_VEC2: michael@0: *unitSize = 2; michael@0: break; michael@0: case LOCAL_GL_INT_VEC3: michael@0: case LOCAL_GL_FLOAT_VEC3: michael@0: case LOCAL_GL_BOOL_VEC3: michael@0: *unitSize = 3; michael@0: break; michael@0: case LOCAL_GL_INT_VEC4: michael@0: case LOCAL_GL_FLOAT_VEC4: michael@0: case LOCAL_GL_BOOL_VEC4: michael@0: *unitSize = 4; michael@0: break; michael@0: case LOCAL_GL_FLOAT_MAT2: michael@0: *unitSize = 4; michael@0: break; michael@0: case LOCAL_GL_FLOAT_MAT3: michael@0: *unitSize = 9; michael@0: break; michael@0: case LOCAL_GL_FLOAT_MAT4: michael@0: *unitSize = 16; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: michael@0: WebGLTexelFormat mozilla::GetWebGLTexelFormat(GLenum internalformat, GLenum type) michael@0: { michael@0: // michael@0: // WEBGL_depth_texture michael@0: if (internalformat == LOCAL_GL_DEPTH_COMPONENT) { michael@0: switch (type) { michael@0: case LOCAL_GL_UNSIGNED_SHORT: michael@0: return WebGLTexelFormat::D16; michael@0: case LOCAL_GL_UNSIGNED_INT: michael@0: return WebGLTexelFormat::D32; michael@0: } michael@0: michael@0: MOZ_CRASH("Invalid WebGL texture format/type?"); michael@0: } michael@0: michael@0: if (internalformat == LOCAL_GL_DEPTH_STENCIL) { michael@0: switch (type) { michael@0: case LOCAL_GL_UNSIGNED_INT_24_8_EXT: michael@0: return WebGLTexelFormat::D24S8; michael@0: } michael@0: michael@0: MOZ_CRASH("Invalid WebGL texture format/type?"); michael@0: } michael@0: michael@0: if (internalformat == LOCAL_GL_DEPTH_COMPONENT16) { michael@0: return WebGLTexelFormat::D16; michael@0: } michael@0: michael@0: if (internalformat == LOCAL_GL_DEPTH_COMPONENT32) { michael@0: return WebGLTexelFormat::D32; michael@0: } michael@0: michael@0: if (internalformat == LOCAL_GL_DEPTH24_STENCIL8) { michael@0: return WebGLTexelFormat::D24S8; michael@0: } michael@0: michael@0: if (type == LOCAL_GL_UNSIGNED_BYTE) { michael@0: switch (internalformat) { michael@0: case LOCAL_GL_RGBA: michael@0: case LOCAL_GL_SRGB_ALPHA_EXT: michael@0: return WebGLTexelFormat::RGBA8; michael@0: case LOCAL_GL_RGB: michael@0: case LOCAL_GL_SRGB_EXT: michael@0: return WebGLTexelFormat::RGB8; michael@0: case LOCAL_GL_ALPHA: michael@0: return WebGLTexelFormat::A8; michael@0: case LOCAL_GL_LUMINANCE: michael@0: return WebGLTexelFormat::R8; michael@0: case LOCAL_GL_LUMINANCE_ALPHA: michael@0: return WebGLTexelFormat::RA8; michael@0: } michael@0: michael@0: MOZ_CRASH("Invalid WebGL texture format/type?"); michael@0: } michael@0: michael@0: if (type == LOCAL_GL_FLOAT) { michael@0: // OES_texture_float michael@0: switch (internalformat) { michael@0: case LOCAL_GL_RGBA: michael@0: case LOCAL_GL_RGBA32F: michael@0: return WebGLTexelFormat::RGBA32F; michael@0: case LOCAL_GL_RGB: michael@0: case LOCAL_GL_RGB32F: michael@0: return WebGLTexelFormat::RGB32F; michael@0: case LOCAL_GL_ALPHA: michael@0: case LOCAL_GL_ALPHA32F_ARB: michael@0: return WebGLTexelFormat::A32F; michael@0: case LOCAL_GL_LUMINANCE: michael@0: case LOCAL_GL_LUMINANCE32F_ARB: michael@0: return WebGLTexelFormat::R32F; michael@0: case LOCAL_GL_LUMINANCE_ALPHA: michael@0: case LOCAL_GL_LUMINANCE_ALPHA32F_ARB: michael@0: return WebGLTexelFormat::RA32F; michael@0: } michael@0: michael@0: MOZ_CRASH("Invalid WebGL texture format/type?"); michael@0: } else if (type == LOCAL_GL_HALF_FLOAT_OES) { michael@0: // OES_texture_half_float michael@0: switch (internalformat) { michael@0: case LOCAL_GL_RGBA: michael@0: case LOCAL_GL_RGBA16F: michael@0: return WebGLTexelFormat::RGBA16F; michael@0: case LOCAL_GL_RGB: michael@0: case LOCAL_GL_RGB16F: michael@0: return WebGLTexelFormat::RGB16F; michael@0: case LOCAL_GL_ALPHA: michael@0: case LOCAL_GL_ALPHA16F_ARB: michael@0: return WebGLTexelFormat::A16F; michael@0: case LOCAL_GL_LUMINANCE: michael@0: case LOCAL_GL_LUMINANCE16F_ARB: michael@0: return WebGLTexelFormat::R16F; michael@0: case LOCAL_GL_LUMINANCE_ALPHA: michael@0: case LOCAL_GL_LUMINANCE_ALPHA16F_ARB: michael@0: return WebGLTexelFormat::RA16F; michael@0: default: michael@0: MOZ_ASSERT(false, "Coding mistake?! Should never reach this point."); michael@0: return WebGLTexelFormat::BadFormat; michael@0: } michael@0: } michael@0: michael@0: switch (type) { michael@0: case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: michael@0: return WebGLTexelFormat::RGBA4444; michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: michael@0: return WebGLTexelFormat::RGBA5551; michael@0: case LOCAL_GL_UNSIGNED_SHORT_5_6_5: michael@0: return WebGLTexelFormat::RGB565; michael@0: default: michael@0: MOZ_ASSERT(false, "Coding mistake?! Should never reach this point."); michael@0: return WebGLTexelFormat::BadFormat; michael@0: } michael@0: michael@0: MOZ_CRASH("Invalid WebGL texture format/type?"); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::BlendColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) { michael@0: if (IsContextLost()) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fBlendColor(r, g, b, a); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Flush() { michael@0: if (IsContextLost()) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fFlush(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::Finish() { michael@0: if (IsContextLost()) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fFinish(); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::LineWidth(GLfloat width) { michael@0: if (IsContextLost()) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fLineWidth(width); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) { michael@0: if (IsContextLost()) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fPolygonOffset(factor, units); michael@0: } michael@0: michael@0: void michael@0: WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) { michael@0: if (IsContextLost()) michael@0: return; michael@0: MakeContextCurrent(); michael@0: gl->fSampleCoverage(value, invert); michael@0: }