michael@0: /* -*- Mode: C++; tab-width: 20; 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 "WebGLTexture.h" michael@0: #include "GLContext.h" michael@0: #include "ScopedGLHelpers.h" michael@0: #include "WebGLTexelConversions.h" michael@0: #include "mozilla/dom/WebGLRenderingContextBinding.h" michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: michael@0: JSObject* michael@0: WebGLTexture::WrapObject(JSContext *cx) { michael@0: return dom::WebGLTextureBinding::Wrap(cx, this); michael@0: } michael@0: michael@0: WebGLTexture::WebGLTexture(WebGLContext *context) michael@0: : WebGLContextBoundObject(context) michael@0: , mHasEverBeenBound(false) michael@0: , mTarget(0) michael@0: , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR) michael@0: , mMagFilter(LOCAL_GL_LINEAR) michael@0: , mWrapS(LOCAL_GL_REPEAT) michael@0: , mWrapT(LOCAL_GL_REPEAT) michael@0: , mFacesCount(0) michael@0: , mMaxLevelWithCustomImages(0) michael@0: , mHaveGeneratedMipmap(false) michael@0: , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture) michael@0: { michael@0: SetIsDOMBinding(); michael@0: mContext->MakeContextCurrent(); michael@0: mContext->gl->fGenTextures(1, &mGLName); michael@0: mContext->mTextures.insertBack(this); michael@0: } michael@0: michael@0: void michael@0: WebGLTexture::Delete() { michael@0: mImageInfos.Clear(); michael@0: mContext->MakeContextCurrent(); michael@0: mContext->gl->fDeleteTextures(1, &mGLName); michael@0: LinkedListElement::removeFrom(mContext->mTextures); michael@0: } michael@0: michael@0: int64_t michael@0: WebGLTexture::ImageInfo::MemoryUsage() const { michael@0: if (mImageDataStatus == WebGLImageDataStatus::NoImageData) michael@0: return 0; michael@0: int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mWebGLFormat, mWebGLType); michael@0: return int64_t(mWidth) * int64_t(mHeight) * bitsPerTexel/8; michael@0: } michael@0: michael@0: int64_t michael@0: WebGLTexture::MemoryUsage() const { michael@0: if (IsDeleted()) michael@0: return 0; michael@0: int64_t result = 0; michael@0: for(size_t face = 0; face < mFacesCount; face++) { michael@0: if (mHaveGeneratedMipmap) { michael@0: // Each mipmap level is 1/4 the size of the previous level michael@0: // 1 + x + x^2 + ... = 1/(1-x) michael@0: // for x = 1/4, we get 1/(1-1/4) = 4/3 michael@0: result += ImageInfoAtFace(face, 0).MemoryUsage() * 4 / 3; michael@0: } else { michael@0: for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++) michael@0: result += ImageInfoAtFace(face, level).MemoryUsage(); michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: WebGLTexture::DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(GLenum texImageTarget) const { michael@0: if (mHaveGeneratedMipmap) michael@0: return true; michael@0: michael@0: // We want a copy here so we can modify it temporarily. michael@0: ImageInfo expected = ImageInfoAt(texImageTarget, 0); michael@0: michael@0: // checks if custom level>0 images are all defined up to the highest level defined michael@0: // and have the expected dimensions michael@0: for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { michael@0: const ImageInfo& actual = ImageInfoAt(texImageTarget, level); michael@0: if (actual != expected) michael@0: return false; michael@0: expected.mWidth = std::max(1, expected.mWidth >> 1); michael@0: expected.mHeight = std::max(1, expected.mHeight >> 1); michael@0: michael@0: // if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence michael@0: // of extra useless levels. michael@0: if (actual.mWidth == 1 && actual.mHeight == 1) michael@0: return true; michael@0: } michael@0: michael@0: // if we're here, we've exhausted all levels without finding a 1x1 image michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: WebGLTexture::Bind(GLenum aTarget) { michael@0: // this function should only be called by bindTexture(). michael@0: // it assumes that the GL context is already current. michael@0: michael@0: bool firstTimeThisTextureIsBound = !mHasEverBeenBound; michael@0: michael@0: if (!firstTimeThisTextureIsBound && aTarget != mTarget) { michael@0: mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target"); michael@0: // very important to return here before modifying texture state! This was the place when I lost a whole day figuring michael@0: // very strange 'invalid write' crashes. michael@0: return; michael@0: } michael@0: michael@0: mTarget = aTarget; michael@0: michael@0: mContext->gl->fBindTexture(mTarget, mGLName); michael@0: michael@0: if (firstTimeThisTextureIsBound) { michael@0: mFacesCount = (mTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6; michael@0: EnsureMaxLevelWithCustomImagesAtLeast(0); michael@0: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); michael@0: michael@0: // thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R is not michael@0: // present in GLES 2, but is present in GL and it seems as if for cube maps michael@0: // we need to set it to GL_CLAMP_TO_EDGE to get the expected GLES behavior. michael@0: if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES()) michael@0: mContext->gl->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); michael@0: } michael@0: michael@0: mHasEverBeenBound = true; michael@0: } michael@0: michael@0: void michael@0: WebGLTexture::SetImageInfo(GLenum aTarget, GLint aLevel, michael@0: GLsizei aWidth, GLsizei aHeight, michael@0: GLenum aFormat, GLenum aType, WebGLImageDataStatus aStatus) michael@0: { michael@0: if ( (aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D) ) michael@0: return; michael@0: michael@0: EnsureMaxLevelWithCustomImagesAtLeast(aLevel); michael@0: michael@0: ImageInfoAt(aTarget, aLevel) = ImageInfo(aWidth, aHeight, aFormat, aType, aStatus); michael@0: michael@0: if (aLevel > 0) michael@0: SetCustomMipmap(); michael@0: michael@0: // Invalidate framebuffer status cache michael@0: NotifyFBsStatusChanged(); michael@0: michael@0: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); michael@0: } michael@0: michael@0: void michael@0: WebGLTexture::SetGeneratedMipmap() { michael@0: if (!mHaveGeneratedMipmap) { michael@0: mHaveGeneratedMipmap = true; michael@0: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); michael@0: } michael@0: } michael@0: michael@0: void michael@0: WebGLTexture::SetCustomMipmap() { michael@0: if (mHaveGeneratedMipmap) { michael@0: // if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode, michael@0: // we need to compute now all the mipmap image info. michael@0: michael@0: // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info, michael@0: // and are power-of-two. michael@0: ImageInfo imageInfo = ImageInfoAtFace(0, 0); michael@0: NS_ASSERTION(imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?"); michael@0: michael@0: GLsizei size = std::max(imageInfo.mWidth, imageInfo.mHeight); michael@0: michael@0: // so, the size is a power of two, let's find its log in base 2. michael@0: size_t maxLevel = 0; michael@0: for (GLsizei n = size; n > 1; n >>= 1) michael@0: ++maxLevel; michael@0: michael@0: EnsureMaxLevelWithCustomImagesAtLeast(maxLevel); michael@0: michael@0: for (size_t level = 1; level <= maxLevel; ++level) { michael@0: // again, since the sizes are powers of two, no need for any max(1,x) computation michael@0: imageInfo.mWidth >>= 1; michael@0: imageInfo.mHeight >>= 1; michael@0: for(size_t face = 0; face < mFacesCount; ++face) michael@0: ImageInfoAtFace(face, level) = imageInfo; michael@0: } michael@0: } michael@0: mHaveGeneratedMipmap = false; michael@0: } michael@0: michael@0: bool michael@0: WebGLTexture::AreAllLevel0ImageInfosEqual() const { michael@0: for (size_t face = 1; face < mFacesCount; ++face) { michael@0: if (ImageInfoAtFace(face, 0) != ImageInfoAtFace(0, 0)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: WebGLTexture::IsMipmapTexture2DComplete() const { michael@0: if (mTarget != LOCAL_GL_TEXTURE_2D) michael@0: return false; michael@0: if (!ImageInfoAt(LOCAL_GL_TEXTURE_2D, 0).IsPositive()) michael@0: return false; michael@0: if (mHaveGeneratedMipmap) michael@0: return true; michael@0: return DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D); michael@0: } michael@0: michael@0: bool michael@0: WebGLTexture::IsCubeComplete() const { michael@0: if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP) michael@0: return false; michael@0: const ImageInfo &first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0); michael@0: if (!first.IsPositive() || !first.IsSquare()) michael@0: return false; michael@0: return AreAllLevel0ImageInfosEqual(); michael@0: } michael@0: michael@0: static GLenum michael@0: GLCubeMapFaceById(int id) michael@0: { michael@0: GLenum result = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + id; michael@0: MOZ_ASSERT(result >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X && michael@0: result <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: WebGLTexture::IsMipmapCubeComplete() const { michael@0: if (!IsCubeComplete()) // in particular, this checks that this is a cube map michael@0: return false; michael@0: for (int i = 0; i < 6; i++) { michael@0: GLenum face = GLCubeMapFaceById(i); michael@0: if (!DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(face)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: WebGLTextureFakeBlackStatus michael@0: WebGLTexture::ResolvedFakeBlackStatus() { michael@0: if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) { michael@0: return mFakeBlackStatus; michael@0: } michael@0: michael@0: // Determine if the texture needs to be faked as a black texture. michael@0: // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec. michael@0: michael@0: for (size_t face = 0; face < mFacesCount; ++face) { michael@0: if (ImageInfoAtFace(face, 0).mImageDataStatus == WebGLImageDataStatus::NoImageData) { michael@0: // In case of undefined texture image, we don't print any message because this is a very common michael@0: // and often legitimate case (asynchronous texture loading). michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: return mFakeBlackStatus; michael@0: } michael@0: } michael@0: michael@0: const char *msg_rendering_as_black michael@0: = "A texture is going to be rendered as if it were black, as per the OpenGL ES 2.0.24 spec section 3.8.2, " michael@0: "because it"; michael@0: michael@0: if (mTarget == LOCAL_GL_TEXTURE_2D) michael@0: { michael@0: if (DoesMinFilterRequireMipmap()) michael@0: { michael@0: if (!IsMipmapTexture2DComplete()) { michael@0: mContext->GenerateWarning michael@0: ("%s is a 2D texture, with a minification filter requiring a mipmap, " michael@0: "and is not mipmap complete (as defined in section 3.7.10).", msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } else if (!ImageInfoAt(mTarget, 0).IsPowerOfTwo()) { michael@0: mContext->GenerateWarning michael@0: ("%s is a 2D texture, with a minification filter requiring a mipmap, " michael@0: "and either its width or height is not a power of two.", msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } michael@0: } michael@0: else // no mipmap required michael@0: { michael@0: if (!ImageInfoAt(mTarget, 0).IsPositive()) { michael@0: mContext->GenerateWarning michael@0: ("%s is a 2D texture and its width or height is equal to zero.", michael@0: msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } else if (!AreBothWrapModesClampToEdge() && !ImageInfoAt(mTarget, 0).IsPowerOfTwo()) { michael@0: mContext->GenerateWarning michael@0: ("%s is a 2D texture, with a minification filter not requiring a mipmap, " michael@0: "with its width or height not a power of two, and with a wrap mode " michael@0: "different from CLAMP_TO_EDGE.", msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } michael@0: } michael@0: } michael@0: else // cube map michael@0: { michael@0: bool areAllLevel0ImagesPOT = true; michael@0: for (size_t face = 0; face < mFacesCount; ++face) michael@0: areAllLevel0ImagesPOT &= ImageInfoAtFace(face, 0).IsPowerOfTwo(); michael@0: michael@0: if (DoesMinFilterRequireMipmap()) michael@0: { michael@0: if (!IsMipmapCubeComplete()) { michael@0: mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, " michael@0: "and is not mipmap cube complete (as defined in section 3.7.10).", michael@0: msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } else if (!areAllLevel0ImagesPOT) { michael@0: mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, " michael@0: "and either the width or the height of some level 0 image is not a power of two.", michael@0: msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } michael@0: } michael@0: else // no mipmap required michael@0: { michael@0: if (!IsCubeComplete()) { michael@0: mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, " michael@0: "and is not cube complete (as defined in section 3.7.10).", michael@0: msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } else if (!AreBothWrapModesClampToEdge() && !areAllLevel0ImagesPOT) { michael@0: mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, " michael@0: "with some level 0 image having width or height not a power of two, and with a wrap mode " michael@0: "different from CLAMP_TO_EDGE.", msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (ImageInfoBase().mWebGLType == LOCAL_GL_FLOAT && michael@0: !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear)) michael@0: { michael@0: if (mMinFilter == LOCAL_GL_LINEAR || michael@0: mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR || michael@0: mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST || michael@0: mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR) michael@0: { michael@0: mContext->GenerateWarning("%s is a texture with a linear minification filter, " michael@0: "which is not compatible with gl.FLOAT by default. " michael@0: "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } michael@0: else if (mMagFilter == LOCAL_GL_LINEAR) michael@0: { michael@0: mContext->GenerateWarning("%s is a texture with a linear magnification filter, " michael@0: "which is not compatible with gl.FLOAT by default. " michael@0: "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } michael@0: } else if (ImageInfoBase().mWebGLType == LOCAL_GL_HALF_FLOAT_OES && michael@0: !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear)) michael@0: { michael@0: if (mMinFilter == LOCAL_GL_LINEAR || michael@0: mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR || michael@0: mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST || michael@0: mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR) michael@0: { michael@0: mContext->GenerateWarning("%s is a texture with a linear minification filter, " michael@0: "which is not compatible with gl.HALF_FLOAT by default. " michael@0: "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } michael@0: else if (mMagFilter == LOCAL_GL_LINEAR) michael@0: { michael@0: mContext->GenerateWarning("%s is a texture with a linear magnification filter, " michael@0: "which is not compatible with gl.HALF_FLOAT by default. " michael@0: "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black); michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; michael@0: } michael@0: } michael@0: michael@0: // We have exhausted all cases of incomplete textures, where we would need opaque black. michael@0: // We may still need transparent black in case of uninitialized image data. michael@0: bool hasUninitializedImageData = false; michael@0: for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { michael@0: for (size_t face = 0; face < mFacesCount; ++face) { michael@0: hasUninitializedImageData |= (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::UninitializedImageData); michael@0: } michael@0: } michael@0: michael@0: if (hasUninitializedImageData) { michael@0: bool hasAnyInitializedImageData = false; michael@0: for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { michael@0: for (size_t face = 0; face < mFacesCount; ++face) { michael@0: if (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::InitializedImageData) { michael@0: hasAnyInitializedImageData = true; michael@0: break; michael@0: } michael@0: } michael@0: if (hasAnyInitializedImageData) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (hasAnyInitializedImageData) { michael@0: // The texture contains some initialized image data, and some uninitialized image data. michael@0: // In this case, we have no choice but to initialize all image data now. Fortunately, michael@0: // in this case we know that we can't be dealing with a depth texture per WEBGL_depth_texture michael@0: // and ANGLE_depth_texture (which allow only one image per texture) so we can assume that michael@0: // glTexImage2D is able to upload data to images. michael@0: for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { michael@0: for (size_t face = 0; face < mFacesCount; ++face) { michael@0: GLenum imageTarget = mTarget == LOCAL_GL_TEXTURE_2D michael@0: ? LOCAL_GL_TEXTURE_2D michael@0: : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; michael@0: const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); michael@0: if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) { michael@0: DoDeferredImageInitialization(imageTarget, level); michael@0: } michael@0: } michael@0: } michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; michael@0: } else { michael@0: // The texture only contains uninitialized image data. In this case, michael@0: // we can use a black texture for it. michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData; michael@0: } michael@0: } michael@0: michael@0: // we have exhausted all cases where we do need fakeblack, so if the status is still unknown, michael@0: // that means that we do NOT need it. michael@0: if (mFakeBlackStatus == WebGLTextureFakeBlackStatus::Unknown) { michael@0: mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; michael@0: } michael@0: michael@0: MOZ_ASSERT(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown); michael@0: return mFakeBlackStatus; michael@0: } michael@0: michael@0: void michael@0: WebGLTexture::DoDeferredImageInitialization(GLenum imageTarget, GLint level) michael@0: { michael@0: const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); michael@0: MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData); michael@0: michael@0: mContext->MakeContextCurrent(); michael@0: gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget); michael@0: michael@0: GLenum format = imageInfo.mWebGLFormat; michael@0: GLenum type = imageInfo.mWebGLType; michael@0: WebGLTexelFormat texelformat = GetWebGLTexelFormat(format, type); michael@0: uint32_t texelsize = WebGLTexelConversions::TexelBytesForFormat(texelformat); michael@0: CheckedUint32 checked_byteLength michael@0: = WebGLContext::GetImageSize( michael@0: imageInfo.mHeight, michael@0: imageInfo.mWidth, michael@0: texelsize, michael@0: mContext->mPixelStoreUnpackAlignment); michael@0: MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier michael@0: void *zeros = calloc(1, checked_byteLength.value()); michael@0: michael@0: gl::GLContext* gl = mContext->gl; 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: mContext->GetAndFlushUnderlyingGLErrors(); michael@0: gl->fTexImage2D(imageTarget, level, driverInternalFormat, michael@0: imageInfo.mWidth, imageInfo.mHeight, michael@0: 0, driverFormat, driverType, michael@0: zeros); michael@0: GLenum error = mContext->GetAndFlushUnderlyingGLErrors(); michael@0: michael@0: free(zeros); michael@0: SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData); michael@0: michael@0: if (error) { michael@0: // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here. michael@0: MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past. michael@0: return; michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef) michael@0: NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)